Repository: lightningnetwork/lnd
Pull request #8117 (2023-10-26 12:35:50)

channeldb+refactor: remove `kvdb.Backend` from channel DB schemas
## Overview

Today, the `channeldb.LightningNode`, `channeldb.ChannelEdgePolicy` and 
`channeldb.ChannelEdgeInfo` objects all carry a `kvdb.Backend` pointer. 
Additionally, the `ChannelEdgePolicy` has a `channeldb.LightningNode` 
member. 

This PR is a move towards making the above mentioned `channeldb` structs 
more closely represent the on-disk schemas and does this by removing the
need for them to carry around the `kvdb.Backend` pointer and also removes 
the need for `channeldb.ChannelEdgeInfo` to contain a 
`channeldb.LightningNode`. 

## Why?

For the [Gossip 1.75 epic](https://github.com/lightningnetwork/lnd/issues/7961), it will be required to create abstractions of the mentioned
objects and so in order to avoid requiring interface methods like "GetDB()" and 
"SetDB()", removing the db entirely from the objects will make for much simpler code. 

I think this might also be useful for when we migrate channeldb to the sql world.

ellemouton: realised that with this change we can actually move the `ChannelEdgeInfo`, `ChannelEdgePolicy`, `ChannelAuthProof` and `CachedEdgePolicy` structs to the `channeldb/models` package instead. 

Lemme know what you guys think & then I'll add a commit on. 
bhandras: > realised that with this change we can actually move the `ChannelEdgeInfo`, `ChannelEdgePolicy`, `ChannelAuthProof` and `CachedEdgePolicy` structs to the `channeldb/models` package instead.
> 
> Lemme know what you guys think & then I'll add a commit on.



> realised that with this change we can actually move the `ChannelEdgeInfo`, `ChannelEdgePolicy`, `ChannelAuthProof` and `CachedEdgePolicy` structs to the `channeldb/models` package instead.
> 
> Lemme know what you guys think & then I'll add a commit on.

What would be the benefit of this move?
lightninglabs-deploy: @ellemouton, remember to re-request review from reviewers when ready

Patch:
diff --git a/autopilot/graph.go b/autopilot/graph.go
index 0b062a535..74b04c503 100644
--- a/autopilot/graph.go
+++ b/autopilot/graph.go
@@ -12,6 +12,7 @@ import (
 	"github.com/btcsuite/btcd/btcec/v2/ecdsa"
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -53,6 +54,8 @@ func ChannelGraphFromDatabase(db *channeldb.ChannelGraph) ChannelGraph {
 // channeldb.LightningNode. The wrapper method implement the autopilot.Node
 // interface.
 type dbNode struct {
+	db *channeldb.ChannelGraph
+
 	tx kvdb.RTx
 
 	node *channeldb.LightningNode
@@ -67,7 +70,7 @@ var _ Node = (*dbNode)(nil)
 // will be returned in serialized compressed format.
 //
 // NOTE: Part of the autopilot.Node interface.
-func (d dbNode) PubKey() [33]byte {
+func (d *dbNode) PubKey() [33]byte {
 	return d.node.PubKeyBytes
 }
 
@@ -75,7 +78,7 @@ func (d dbNode) PubKey() [33]byte {
 // peer is known to be listening on.
 //
 // NOTE: Part of the autopilot.Node interface.
-func (d dbNode) Addrs() []net.Addr {
+func (d *dbNode) Addrs() []net.Addr {
 	return d.node.Addresses
 }
 
@@ -85,32 +88,42 @@ func (d dbNode) Addrs() []net.Addr {
 // describes the active channel.
 //
 // NOTE: Part of the autopilot.Node interface.
-func (d dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
-	return d.node.ForEachChannel(d.tx, func(tx kvdb.RTx,
-		ei *channeldb.ChannelEdgeInfo, ep, _ *channeldb.ChannelEdgePolicy) error {
-
-		// Skip channels for which no outgoing edge policy is available.
-		//
-		// TODO(joostjager): Ideally the case where channels have a nil
-		// policy should be supported, as autopilot is not looking at
-		// the policies. For now, it is not easily possible to get a
-		// reference to the other end LightningNode object without
-		// retrieving the policy.
-		if ep == nil {
-			return nil
-		}
+func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
+	return d.db.ForEachNodeChannel(d.tx, d.node.PubKeyBytes,
+		func(tx kvdb.RTx, ei *models.ChannelEdgeInfo, ep,
+			_ *models.ChannelEdgePolicy) error {
+
+			// Skip channels for which no outgoing edge policy is
+			// available.
+			//
+			// TODO(joostjager): Ideally the case where channels
+			// have a nil policy should be supported, as autopilot
+			// is not looking at the policies. For now, it is not
+			// easily possible to get a reference to the other end
+			// LightningNode object without retrieving the policy.
+			if ep == nil {
+				return nil
+			}
 
-		edge := ChannelEdge{
-			ChanID:   lnwire.NewShortChanIDFromInt(ep.ChannelID),
-			Capacity: ei.Capacity,
-			Peer: dbNode{
-				tx:   tx,
-				node: ep.Node,
-			},
-		}
+			node, err := d.db.FetchLightningNode(tx, ep.ToNode)
+			if err != nil {
+				return err
+			}
 
-		return cb(edge)
-	})
+			edge := ChannelEdge{
+				ChanID: lnwire.NewShortChanIDFromInt(
+					ep.ChannelID,
+				),
+				Capacity: ei.Capacity,
+				Peer: &dbNode{
+					tx:   tx,
+					db:   d.db,
+					node: node,
+				},
+			}
+
+			return cb(edge)
+		})
 }
 
 // ForEachNode is a higher-order function that should be called once for each
@@ -127,7 +140,8 @@ func (d *databaseChannelGraph) ForEachNode(cb func(Node) error) error {
 			return nil
 		}
 
-		node := dbNode{
+		node := &dbNode{
+			db:   d.db,
 			tx:   tx,
 			node: n,
 		}
@@ -150,7 +164,7 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
 				return nil, err
 			}
 
-			dbNode, err := d.db.FetchLightningNode(vertex)
+			dbNode, err := d.db.FetchLightningNode(nil, vertex)
 			switch {
 			case err == channeldb.ErrGraphNodeNotFound:
 				fallthrough
@@ -222,7 +236,7 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
 	}
 
 	chanID := randChanID()
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID: chanID.ToUint64(),
 		Capacity:  capacity,
 	}
@@ -230,7 +244,7 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
 	if err := d.db.AddChannelEdge(edge); err != nil {
 		return nil, nil, err
 	}
-	edgePolicy := &channeldb.ChannelEdgePolicy{
+	edgePolicy := &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 chanID.ToUint64(),
 		LastUpdate:                time.Now(),
@@ -246,7 +260,7 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
 	if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
 		return nil, nil, err
 	}
-	edgePolicy = &channeldb.ChannelEdgePolicy{
+	edgePolicy = &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 chanID.ToUint64(),
 		LastUpdate:                time.Now(),
@@ -265,14 +279,16 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
 	return &ChannelEdge{
 			ChanID:   chanID,
 			Capacity: capacity,
-			Peer: dbNode{
+			Peer: &dbNode{
+				db:   d.db,
 				node: vertex1,
 			},
 		},
 		&ChannelEdge{
 			ChanID:   chanID,
 			Capacity: capacity,
-			Peer: dbNode{
+			Peer: &dbNode{
+				db:   d.db,
 				node: vertex2,
 			},
 		},
diff --git a/channeldb/db.go b/channeldb/db.go
index bf170e2ba..d1b9f684e 100644
--- a/channeldb/db.go
+++ b/channeldb/db.go
@@ -1351,7 +1351,7 @@ func (d *DB) AddrsForNode(nodePub *btcec.PublicKey) ([]net.Addr,
 	if err != nil {
 		return nil, err
 	}
-	graphNode, err := d.graph.FetchLightningNode(pubKey)
+	graphNode, err := d.graph.FetchLightningNode(nil, pubKey)
 	if err != nil && err != ErrGraphNodeNotFound {
 		return nil, err
 	} else if err == ErrGraphNodeNotFound {
diff --git a/channeldb/graph.go b/channeldb/graph.go
index 8367aaf68..c0cbc3287 100644
--- a/channeldb/graph.go
+++ b/channeldb/graph.go
@@ -16,12 +16,12 @@ import (
 
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/btcec/v2/ecdsa"
-	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/txscript"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/lightningnetwork/lnd/aliasmgr"
 	"github.com/lightningnetwork/lnd/batch"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/input"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -161,9 +161,6 @@ const (
 	// would be possible for a node to create a ton of updates and slowly
 	// fill our disk, and also waste bandwidth due to relaying.
 	MaxAllowedExtraOpaqueBytes = 10000
-
-	// feeRateParts is the total number of parts used to express fee rates.
-	feeRateParts = 1e6
 )
 
 // ChannelGraph is a persistent, on-disk graph representation of the Lightning
@@ -231,8 +228,8 @@ func NewChannelGraph(db kvdb.Backend, rejectCacheSize, chanCacheSize int,
 			return nil, err
 		}
 
-		err = g.ForEachChannel(func(info *ChannelEdgeInfo,
-			policy1, policy2 *ChannelEdgePolicy) error {
+		err = g.ForEachChannel(func(info *models.ChannelEdgeInfo,
+			policy1, policy2 *models.ChannelEdgePolicy) error {
 
 			g.graphCache.AddChannel(info, policy1, policy2)
 
@@ -258,10 +255,10 @@ type channelMapKey struct {
 // getChannelMap loads all channel edge policies from the database and stores
 // them in a map.
 func (c *ChannelGraph) getChannelMap(edges kvdb.RBucket) (
-	map[channelMapKey]*ChannelEdgePolicy, error) {
+	map[channelMapKey]*models.ChannelEdgePolicy, error) {
 
 	// Create a map to store all channel edge policies.
-	channelMap := make(map[channelMapKey]*ChannelEdgePolicy)
+	channelMap := make(map[channelMapKey]*models.ChannelEdgePolicy)
 
 	err := kvdb.ForAll(edges, func(k, edgeBytes []byte) error {
 		// Skip embedded buckets.
@@ -411,8 +408,8 @@ func (c *ChannelGraph) NewPathFindTx() (kvdb.RTx, error) {
 // NOTE: If an edge can't be found, or wasn't advertised, then a nil pointer
 // for that particular channel edge routing policy will be passed into the
 // callback.
-func (c *ChannelGraph) ForEachChannel(cb func(*ChannelEdgeInfo,
-	*ChannelEdgePolicy, *ChannelEdgePolicy) error) error {
+func (c *ChannelGraph) ForEachChannel(cb func(*models.ChannelEdgeInfo,
+	*models.ChannelEdgePolicy, *models.ChannelEdgePolicy) error) error {
 
 	return c.db.View(func(tx kvdb.RTx) error {
 		edges := tx.ReadBucket(edgeBucket)
@@ -459,16 +456,14 @@ func (c *ChannelGraph) ForEachChannel(cb func(*ChannelEdgeInfo,
 	}, func() {})
 }
 
-// ForEachNodeChannel iterates through all channels of a given node, executing the
-// passed callback with an edge info structure and the policies of each end
-// of the channel. The first edge policy is the outgoing edge *to* the
-// connecting node, while the second is the incoming edge *from* the
-// connecting node. If the callback returns an error, then the iteration is
-// halted with the error propagated back up to the caller.
+// ForEachNodeDirectedChannel iterates through all channels of a given node,
+// executing the passed callback on the directed edge representing the channel
+// and its incoming policy. If the callback returns an error, then the iteration
+// is halted with the error propagated back up to the caller.
 //
 // Unknown policies are passed into the callback as nil values.
-func (c *ChannelGraph) ForEachNodeChannel(tx kvdb.RTx, node route.Vertex,
-	cb func(channel *DirectedChannel) error) error {
+func (c *ChannelGraph) ForEachNodeDirectedChannel(tx kvdb.RTx,
+	node route.Vertex, cb func(channel *DirectedChannel) error) error {
 
 	if c.graphCache != nil {
 		return c.graphCache.ForEachChannel(node, cb)
@@ -483,12 +478,12 @@ func (c *ChannelGraph) ForEachNodeChannel(tx kvdb.RTx, node route.Vertex,
 		return err
 	}
 
-	dbCallback := func(tx kvdb.RTx, e *ChannelEdgeInfo, p1,
-		p2 *ChannelEdgePolicy) error {
+	dbCallback := func(tx kvdb.RTx, e *models.ChannelEdgeInfo, p1,
+		p2 *models.ChannelEdgePolicy) error {
 
-		var cachedInPolicy *CachedEdgePolicy
+		var cachedInPolicy *models.CachedEdgePolicy
 		if p2 != nil {
-			cachedInPolicy = NewCachedPolicy(p2)
+			cachedInPolicy = models.NewCachedPolicy(p2)
 			cachedInPolicy.ToNodePubKey = toNodeCallback
 			cachedInPolicy.ToNodeFeatures = toNodeFeatures
 		}
@@ -521,7 +516,7 @@ func (c *ChannelGraph) FetchNodeFeatures(
 	}
 
 	// Fallback that uses the database.
-	targetNode, err := c.FetchLightningNode(node)
+	targetNode, err := c.FetchLightningNode(nil, node)
 	switch err {
 	// If the node exists and has features, return them directly.
 	case nil:
@@ -557,44 +552,50 @@ func (c *ChannelGraph) ForEachNodeCached(cb func(node route.Vertex,
 	return c.ForEachNode(func(tx kvdb.RTx, node *LightningNode) error {
 		channels := make(map[uint64]*DirectedChannel)
 
-		err := node.ForEachChannel(tx, func(tx kvdb.RTx,
-			e *ChannelEdgeInfo, p1 *ChannelEdgePolicy,
-			p2 *ChannelEdgePolicy) error {
+		err := c.ForEachNodeChannel(tx, node.PubKeyBytes,
+			func(tx kvdb.RTx, e *models.ChannelEdgeInfo,
+				p1 *models.ChannelEdgePolicy,
+				p2 *models.ChannelEdgePolicy) error {
 
-			toNodeCallback := func() route.Vertex {
-				return node.PubKeyBytes
-			}
-			toNodeFeatures, err := c.FetchNodeFeatures(
-				node.PubKeyBytes,
-			)
-			if err != nil {
-				return err
-			}
+				toNodeCallback := func() route.Vertex {
+					return node.PubKeyBytes
+				}
+				toNodeFeatures, err := c.FetchNodeFeatures(
+					node.PubKeyBytes,
+				)
+				if err != nil {
+					return err
+				}
 
-			var cachedInPolicy *CachedEdgePolicy
-			if p2 != nil {
-				cachedInPolicy := NewCachedPolicy(p2)
-				cachedInPolicy.ToNodePubKey = toNodeCallback
-				cachedInPolicy.ToNodeFeatures = toNodeFeatures
-			}
+				var cachedInPolicy *models.CachedEdgePolicy
+				if p2 != nil {
+					cachedInPolicy =
+						models.NewCachedPolicy(p2)
+					cachedInPolicy.ToNodePubKey =
+						toNodeCallback
+					cachedInPolicy.ToNodeFeatures =
+						toNodeFeatures
+				}
 
-			directedChannel := &DirectedChannel{
-				ChannelID:    e.ChannelID,
-				IsNode1:      node.PubKeyBytes == e.NodeKey1Bytes,
-				OtherNode:    e.NodeKey2Bytes,
-				Capacity:     e.Capacity,
-				OutPolicySet: p1 != nil,
-				InPolicy:     cachedInPolicy,
-			}
+				directedChannel := &DirectedChannel{
+					ChannelID: e.ChannelID,
+					IsNode1: node.PubKeyBytes ==
+						e.NodeKey1Bytes,
+					OtherNode:    e.NodeKey2Bytes,
+					Capacity:     e.Capacity,
+					OutPolicySet: p1 != nil,
+					InPolicy:     cachedInPolicy,
+				}
 
-			if node.PubKeyBytes == e.NodeKey2Bytes {
-				directedChannel.OtherNode = e.NodeKey1Bytes
-			}
+				if node.PubKeyBytes == e.NodeKey2Bytes {
+					directedChannel.OtherNode =
+						e.NodeKey1Bytes
+				}
 
-			channels[e.ChannelID] = directedChannel
+				channels[e.ChannelID] = directedChannel
 
-			return nil
-		})
+				return nil
+			})
 		if err != nil {
 			return err
 		}
@@ -679,7 +680,6 @@ func (c *ChannelGraph) ForEachNode(
 			if err != nil {
 				return err
 			}
-			node.db = c.db
 
 			// Execute the callback, the transaction will abort if
 			// this returns an error.
@@ -777,7 +777,6 @@ func (c *ChannelGraph) sourceNode(nodes kvdb.RBucket) (*LightningNode, error) {
 	if err != nil {
 		return nil, err
 	}
-	node.db = c.db
 
 	return &node, nil
 }
@@ -967,7 +966,7 @@ func (c *ChannelGraph) deleteLightningNode(nodes kvdb.RwBucket,
 // involved in creation of the channel, and the set of features that the channel
 // supports. The chanPoint and chanID are used to uniquely identify the edge
 // globally within the database.
-func (c *ChannelGraph) AddChannelEdge(edge *ChannelEdgeInfo,
+func (c *ChannelGraph) AddChannelEdge(edge *models.ChannelEdgeInfo,
 	op ...batch.SchedulerOption) error {
 
 	var alreadyExists bool
@@ -1010,7 +1009,9 @@ func (c *ChannelGraph) AddChannelEdge(edge *ChannelEdgeInfo,
 
 // addChannelEdge is the private form of AddChannelEdge that allows callers to
 // utilize an existing db transaction.
-func (c *ChannelGraph) addChannelEdge(tx kvdb.RwTx, edge *ChannelEdgeInfo) error {
+func (c *ChannelGraph) addChannelEdge(tx kvdb.RwTx,
+	edge *models.ChannelEdgeInfo) error {
+
 	// Construct the channel's primary key which is the 8-byte channel ID.
 	var chanKey [8]byte
 	binary.BigEndian.PutUint64(chanKey[:], edge.ChannelID)
@@ -1186,8 +1187,9 @@ func (c *ChannelGraph) HasChannelEdge(
 			return ErrGraphNodeNotFound
 		}
 
-		e1, e2, err := fetchChanEdgePolicies(edgeIndex, edges, nodes,
-			channelID[:], c.db)
+		e1, e2, err := fetchChanEdgePolicies(
+			edgeIndex, edges, channelID[:],
+		)
 		if err != nil {
 			return err
 		}
@@ -1220,7 +1222,7 @@ func (c *ChannelGraph) HasChannelEdge(
 // In order to maintain this constraints, we return an error in the scenario
 // that an edge info hasn't yet been created yet, but someone attempts to update
 // it.
-func (c *ChannelGraph) UpdateChannelEdge(edge *ChannelEdgeInfo) error {
+func (c *ChannelGraph) UpdateChannelEdge(edge *models.ChannelEdgeInfo) error {
 	// Construct the channel's primary key which is the 8-byte channel ID.
 	var chanKey [8]byte
 	binary.BigEndian.PutUint64(chanKey[:], edge.ChannelID)
@@ -1265,12 +1267,13 @@ const (
 // with the current UTXO state. A slice of channels that have been closed by
 // the target block are returned if the function succeeds without error.
 func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
-	blockHash *chainhash.Hash, blockHeight uint32) ([]*ChannelEdgeInfo, error) {
+	blockHash *chainhash.Hash, blockHeight uint32) (
+	[]*models.ChannelEdgeInfo, error) {
 
 	c.cacheMu.Lock()
 	defer c.cacheMu.Unlock()
 
-	var chansClosed []*ChannelEdgeInfo
+	var chansClosed []*models.ChannelEdgeInfo
 
 	err := kvdb.Update(c.db, func(tx kvdb.RwTx) error {
 		// First grab the edges bucket which houses the information
@@ -1518,8 +1521,8 @@ func (c *ChannelGraph) pruneGraphNodes(nodes kvdb.RwBucket,
 // set to the last prune height valid for the remaining chain.
 // Channels that were removed from the graph resulting from the
 // disconnected block are returned.
-func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) ([]*ChannelEdgeInfo,
-	error) {
+func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) (
+	[]*models.ChannelEdgeInfo, error) {
 
 	// Every channel having a ShortChannelID starting at 'height'
 	// will no longer be confirmed.
@@ -1541,7 +1544,7 @@ func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) ([]*ChannelEdgeInf
 	defer c.cacheMu.Unlock()
 
 	// Keep track of the channels that are removed from the graph.
-	var removedChans []*ChannelEdgeInfo
+	var removedChans []*models.ChannelEdgeInfo
 
 	if err := kvdb.Update(c.db, func(tx kvdb.RwTx) error {
 		edges, err := tx.CreateTopLevelBucket(edgeBucket)
@@ -1848,15 +1851,23 @@ func (c *ChannelGraph) HighestChanID() (uint64, error) {
 // edge as well as each of the known advertised edge policies.
 type ChannelEdge struct {
 	// Info contains all the static information describing the channel.
-	Info *ChannelEdgeInfo
+	Info *models.ChannelEdgeInfo
 
 	// Policy1 points to the "first" edge policy of the channel containing
 	// the dynamic information required to properly route through the edge.
-	Policy1 *ChannelEdgePolicy
+	Policy1 *models.ChannelEdgePolicy
 
 	// Policy2 points to the "second" edge policy of the channel containing
 	// the dynamic information required to properly route through the edge.
-	Policy2 *ChannelEdgePolicy
+	Policy2 *models.ChannelEdgePolicy
+
+	// Node1 is "node 1" in the channel. This is the node that would have
+	// produced Policy1 if it exists.
+	Node1 *LightningNode
+
+	// Node2 is "node 2" in the channel. This is the node that would have
+	// produced Policy2 if it exists.
+	Node2 *LightningNode
 }
 
 // ChanUpdatesInHorizon returns all the known channel edges which have at least
@@ -1938,12 +1949,11 @@ func (c *ChannelGraph) ChanUpdatesInHorizon(startTime,
 				return fmt.Errorf("unable to fetch info for "+
 					"edge with chan_id=%v: %v", chanID, err)
 			}
-			edgeInfo.db = c.db
 
 			// With the static information obtained, we'll now
 			// fetch the dynamic policy info.
 			edge1, edge2, err := fetchChanEdgePolicies(
-				edgeIndex, edges, nodes, chanID, c.db,
+				edgeIndex, edges, chanID,
 			)
 			if err != nil {
 				chanID := byteOrder.Uint64(chanID)
@@ -1952,6 +1962,20 @@ func (c *ChannelGraph) ChanUpdatesInHorizon(startTime,
 					err)
 			}
 
+			node1, err := fetchLightningNode(
+				nodes, edgeInfo.NodeKey1Bytes[:],
+			)
+			if err != nil {
+				return err
+			}
+
+			node2, err := fetchLightningNode(
+				nodes, edgeInfo.NodeKey2Bytes[:],
+			)
+			if err != nil {
+				return err
+			}
+
 			// Finally, we'll collate this edge with the rest of
 			// edges to be returned.
 			edgesSeen[chanIDInt] = struct{}{}
@@ -1959,6 +1983,8 @@ func (c *ChannelGraph) ChanUpdatesInHorizon(startTime,
 				Info:    &edgeInfo,
 				Policy1: edge1,
 				Policy2: edge2,
+				Node1:   &node1,
+				Node2:   &node2,
 			}
 			edgesInHorizon = append(edgesInHorizon, channel)
 			edgesToCache[chanIDInt] = channel
@@ -2035,7 +2061,6 @@ func (c *ChannelGraph) NodeUpdatesInHorizon(startTime,
 			if err != nil {
 				return err
 			}
-			node.db = c.db
 
 			nodesInHorizon = append(nodesInHorizon, node)
 		}
@@ -2270,12 +2295,25 @@ func (c *ChannelGraph) FetchChanInfos(chanIDs []uint64) ([]ChannelEdge, error) {
 			case err != nil:
 				return err
 			}
-			edgeInfo.db = c.db
 
 			// With the static information obtained, we'll now
 			// fetch the dynamic policy info.
 			edge1, edge2, err := fetchChanEdgePolicies(
-				edgeIndex, edges, nodes, cidBytes[:], c.db,
+				edgeIndex, edges, cidBytes[:],
+			)
+			if err != nil {
+				return err
+			}
+
+			node1, err := fetchLightningNode(
+				nodes, edgeInfo.NodeKey1Bytes[:],
+			)
+			if err != nil {
+				return err
+			}
+
+			node2, err := fetchLightningNode(
+				nodes, edgeInfo.NodeKey2Bytes[:],
 			)
 			if err != nil {
 				return err
@@ -2285,6 +2323,8 @@ func (c *ChannelGraph) FetchChanInfos(chanIDs []uint64) ([]ChannelEdge, error) {
 				Info:    &edgeInfo,
 				Policy1: edge1,
 				Policy2: edge2,
+				Node1:   &node1,
+				Node2:   &node2,
 			})
 		}
 		return nil
@@ -2299,7 +2339,7 @@ func (c *ChannelGraph) FetchChanInfos(chanIDs []uint64) ([]ChannelEdge, error) {
 }
 
 func delEdgeUpdateIndexEntry(edgesBucket kvdb.RwBucket, chanID uint64,
-	edge1, edge2 *ChannelEdgePolicy) error {
+	edge1, edge2 *models.ChannelEdgePolicy) error {
 
 	// First, we'll fetch the edge update index bucket which currently
 	// stores an entry for the channel we're about to delete.
@@ -2355,9 +2395,7 @@ func (c *ChannelGraph) delChannelEdge(edges, edgeIndex, chanIndex, zombieIndex,
 	// we delete the edges themselves so we can access their last update
 	// times.
 	cid := byteOrder.Uint64(chanID)
-	edge1, edge2, err := fetchChanEdgePolicies(
-		edgeIndex, edges, nodes, chanID, nil,
-	)
+	edge1, edge2, err := fetchChanEdgePolicies(edgeIndex, edges, chanID)
 	if err != nil {
 		return err
 	}
@@ -2438,8 +2476,8 @@ func (c *ChannelGraph) delChannelEdge(edges, edgeIndex, chanIndex, zombieIndex,
 // the channel. If the channel were to be marked zombie again, it would be
 // marked with the correct lagging channel since we received an update from only
 // one side.
-func makeZombiePubkeys(info *ChannelEdgeInfo,
-	e1, e2 *ChannelEdgePolicy) ([33]byte, [33]byte) {
+func makeZombiePubkeys(info *models.ChannelEdgeInfo,
+	e1, e2 *models.ChannelEdgePolicy) ([33]byte, [33]byte) {
 
 	switch {
 	// If we don't have either edge policy, we'll return both pubkeys so
@@ -2469,7 +2507,7 @@ func makeZombiePubkeys(info *ChannelEdgeInfo,
 // updated, otherwise it's the second node's information. The node ordering is
 // determined by the lexicographical ordering of the identity public keys of the
 // nodes on either side of the channel.
-func (c *ChannelGraph) UpdateEdgePolicy(edge *ChannelEdgePolicy,
+func (c *ChannelGraph) UpdateEdgePolicy(edge *models.ChannelEdgePolicy,
 	op ...batch.SchedulerOption) error {
 
 	var (
@@ -2517,7 +2555,9 @@ func (c *ChannelGraph) UpdateEdgePolicy(edge *ChannelEdgePolicy,
 	return c.chanScheduler.Execute(r)
 }
 
-func (c *ChannelGraph) updateEdgeCache(e *ChannelEdgePolicy, isUpdate1 bool) {
+func (c *ChannelGraph) updateEdgeCache(e *models.ChannelEdgePolicy,
+	isUpdate1 bool) {
+
 	// If an entry for this channel is found in reject cache, we'll modify
 	// the entry with the updated timestamp for the direction that was just
 	// written. If the edge doesn't exist, we'll load the cache entry lazily
@@ -2549,7 +2589,7 @@ func (c *ChannelGraph) updateEdgeCache(e *ChannelEdgePolicy, isUpdate1 bool) {
 // buckets using an existing database transaction. The returned boolean will be
 // true if the updated policy belongs to node1, and false if the policy belonged
 // to node2.
-func updateEdgePolicy(tx kvdb.RwTx, edge *ChannelEdgePolicy,
+func updateEdgePolicy(tx kvdb.RwTx, edge *models.ChannelEdgePolicy,
 	graphCache *GraphCache) (bool, error) {
 
 	edges := tx.ReadWriteBucket(edgeBucket)
@@ -2560,10 +2600,6 @@ func updateEdgePolicy(tx kvdb.RwTx, edge *ChannelEdgePolicy,
 	if edgeIndex == nil {
 		return false, ErrEdgeNotFound
 	}
-	nodes, err := tx.CreateTopLevelBucket(nodeBucket)
-	if err != nil {
-		return false, err
-	}
 
 	// Create the channelID key be converting the channel ID
 	// integer into a byte slice.
@@ -2593,7 +2629,7 @@ func updateEdgePolicy(tx kvdb.RwTx, edge *ChannelEdgePolicy,
 
 	// Finally, with the direction of the edge being updated
 	// identified, we update the on-disk edge representation.
-	err = putChanEdgePolicy(edges, nodes, edge, fromNode, toNode)
+	err := putChanEdgePolicy(edges, edge, fromNode, toNode)
 	if err != nil {
 		return false, err
 	}
@@ -2657,8 +2693,6 @@ type LightningNode struct {
 	// compatible manner.
 	ExtraOpaqueData []byte
 
-	db kvdb.Backend
-
 	// TODO(roasbeef): discovery will need storage to keep it's last IP
 	// address and re-announce if interface changes?
 
@@ -2740,15 +2774,18 @@ func (l *LightningNode) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement,
 // isPublic determines whether the node is seen as public within the graph from
 // the source node's point of view. An existing database transaction can also be
 // specified.
-func (l *LightningNode) isPublic(tx kvdb.RTx, sourcePubKey []byte) (bool, error) {
+func (c *ChannelGraph) isPublic(tx kvdb.RTx, nodePub route.Vertex,
+	sourcePubKey []byte) (bool, error) {
+
 	// In order to determine whether this node is publicly advertised within
 	// the graph, we'll need to look at all of its edges and check whether
 	// they extend to any other node than the source node. errDone will be
 	// used to terminate the check early.
 	nodeIsPublic := false
 	errDone := errors.New("done")
-	err := l.ForEachChannel(tx, func(_ kvdb.RTx, info *ChannelEdgeInfo,
-		_, _ *ChannelEdgePolicy) error {
+	err := c.ForEachNodeChannel(tx, nodePub, func(tx kvdb.RTx,
+		info *models.ChannelEdgeInfo, _ *models.ChannelEdgePolicy,
+		_ *models.ChannelEdgePolicy) error {
 
 		// If this edge doesn't extend to the source node, we'll
 		// terminate our search as we can now conclude that the node is
@@ -2780,12 +2817,13 @@ func (l *LightningNode) isPublic(tx kvdb.RTx, sourcePubKey []byte) (bool, error)
 
 // FetchLightningNode attempts to look up a target node by its identity public
 // key. If the node isn't found in the database, then ErrGraphNodeNotFound is
-// returned.
-func (c *ChannelGraph) FetchLightningNode(nodePub route.Vertex) (
+// returned. An optional transaction may be provided. If none is provided, then
+// a new one will be created.
+func (c *ChannelGraph) FetchLightningNode(tx kvdb.RTx, nodePub route.Vertex) (
 	*LightningNode, error) {
 
 	var node *LightningNode
-	err := kvdb.View(c.db, func(tx kvdb.RTx) error {
+	fetch := func(tx kvdb.RTx) error {
 		// First grab the nodes bucket which stores the mapping from
 		// pubKey to node information.
 		nodes := tx.ReadBucket(nodeBucket)
@@ -2807,14 +2845,26 @@ func (c *ChannelGraph) FetchLightningNode(nodePub route.Vertex) (
 		if err != nil {
 			return err
 		}
-		n.db = c.db
 
 		node = &n
 
 		return nil
-	}, func() {
-		node = nil
-	})
+	}
+
+	if tx == nil {
+		err := kvdb.View(
+			c.db, fetch, func() {
+				node = nil
+			},
+		)
+		if err != nil {
+			return nil, err
+		}
+
+		return node, nil
+	}
+
+	err := fetch(tx)
 	if err != nil {
 		return nil, err
 	}
@@ -2858,8 +2908,8 @@ func (n *graphCacheNode) Features() *lnwire.FeatureVector {
 //
 // Unknown policies are passed into the callback as nil values.
 func (n *graphCacheNode) ForEachChannel(tx kvdb.RTx,
-	cb func(kvdb.RTx, *ChannelEdgeInfo, *ChannelEdgePolicy,
-		*ChannelEdgePolicy) error) error {
+	cb func(kvdb.RTx, *models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+		*models.ChannelEdgePolicy) error) error {
 
 	return nodeTraversal(tx, n.pubKeyBytes[:], nil, cb)
 }
@@ -2919,13 +2969,10 @@ func (c *ChannelGraph) HasLightningNode(nodePub [33]byte) (time.Time, bool, erro
 // nodeTraversal is used to traverse all channels of a node given by its
 // public key and passes channel information into the specified callback.
 func nodeTraversal(tx kvdb.RTx, nodePub []byte, db kvdb.Backend,
-	cb func(kvdb.RTx, *ChannelEdgeInfo, *ChannelEdgePolicy, *ChannelEdgePolicy) error) error {
+	cb func(kvdb.RTx, *models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+		*models.ChannelEdgePolicy) error) error {
 
 	traversal := func(tx kvdb.RTx) error {
-		nodes := tx.ReadBucket(nodeBucket)
-		if nodes == nil {
-			return ErrGraphNotFound
-		}
 		edges := tx.ReadBucket(edgeBucket)
 		if edges == nil {
 			return ErrGraphNotFound
@@ -2961,10 +3008,9 @@ func nodeTraversal(tx kvdb.RTx, nodePub []byte, db kvdb.Backend,
 			if err != nil {
 				return err
 			}
-			edgeInfo.db = db
 
 			outgoingPolicy, err := fetchChanEdgePolicy(
-				edges, chanID, nodePub, nodes,
+				edges, chanID, nodePub,
 			)
 			if err != nil {
 				return err
@@ -2976,7 +3022,7 @@ func nodeTraversal(tx kvdb.RTx, nodePub []byte, db kvdb.Backend,
 			}
 
 			incomingPolicy, err := fetchChanEdgePolicy(
-				edges, chanID, otherNode[:], nodes,
+				edges, chanID, otherNode[:],
 			)
 			if err != nil {
 				return err
@@ -3003,10 +3049,10 @@ func nodeTraversal(tx kvdb.RTx, nodePub []byte, db kvdb.Backend,
 	return traversal(tx)
 }
 
-// ForEachChannel iterates through all channels of this node, executing the
-// passed callback with an edge info structure and the policies of each end
-// of the channel. The first edge policy is the outgoing edge *to* the
-// connecting node, while the second is the incoming edge *from* the
+// ForEachNodeChannel iterates through all channels of the given node,
+// executing the passed callback with an edge info structure and the policies
+// of each end of the channel. The first edge policy is the outgoing edge *to*
+// the connecting node, while the second is the incoming edge *from* the
 // connecting node. If the callback returns an error, then the iteration is
 // halted with the error propagated back up to the caller.
 //
@@ -3016,209 +3062,28 @@ func nodeTraversal(tx kvdb.RTx, nodePub []byte, db kvdb.Backend,
 // should be passed as the first argument.  Otherwise the first argument should
 // be nil and a fresh transaction will be created to execute the graph
 // traversal.
-func (l *LightningNode) ForEachChannel(tx kvdb.RTx,
-	cb func(kvdb.RTx, *ChannelEdgeInfo, *ChannelEdgePolicy,
-		*ChannelEdgePolicy) error) error {
-
-	nodePub := l.PubKeyBytes[:]
-	db := l.db
-
-	return nodeTraversal(tx, nodePub, db, cb)
-}
-
-// ChannelEdgeInfo represents a fully authenticated channel along with all its
-// unique attributes. Once an authenticated channel announcement has been
-// processed on the network, then an instance of ChannelEdgeInfo encapsulating
-// the channels attributes is stored. The other portions relevant to routing
-// policy of a channel are stored within a ChannelEdgePolicy for each direction
-// of the channel.
-type ChannelEdgeInfo struct {
-	// 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
-
-	// ChainHash is the hash that uniquely identifies the chain that this
-	// channel was opened within.
-	//
-	// TODO(roasbeef): need to modify db keying for multi-chain
-	//  * must add chain hash to prefix as well
-	ChainHash chainhash.Hash
+func (c *ChannelGraph) ForEachNodeChannel(tx kvdb.RTx, nodePub route.Vertex,
+	cb func(kvdb.RTx, *models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+		*models.ChannelEdgePolicy) error) error {
 
-	// NodeKey1Bytes is the raw public key of the first node.
-	NodeKey1Bytes [33]byte
-	nodeKey1      *btcec.PublicKey
-
-	// NodeKey2Bytes is the raw public key of the first node.
-	NodeKey2Bytes [33]byte
-	nodeKey2      *btcec.PublicKey
-
-	// BitcoinKey1Bytes is the raw public key of the first node.
-	BitcoinKey1Bytes [33]byte
-	bitcoinKey1      *btcec.PublicKey
-
-	// BitcoinKey2Bytes is the raw public key of the first node.
-	BitcoinKey2Bytes [33]byte
-	bitcoinKey2      *btcec.PublicKey
-
-	// Features is an opaque byte slice that encodes the set of channel
-	// specific features that this channel edge supports.
-	Features []byte
-
-	// AuthProof is the authentication proof for this channel. This proof
-	// contains a set of signatures binding four identities, which attests
-	// to the legitimacy of the advertised channel.
-	AuthProof *ChannelAuthProof
-
-	// ChannelPoint is the funding outpoint of the channel. This can be
-	// used to uniquely identify the channel within the channel graph.
-	ChannelPoint wire.OutPoint
-
-	// Capacity is the total capacity of the channel, this is determined by
-	// the value output in the outpoint that created this channel.
-	Capacity btcutil.Amount
-
-	// ExtraOpaqueData is the set of data that was appended to this
-	// message, some of which we may not actually know how to iterate or
-	// parse. By holding onto this data, we ensure that we're able to
-	// properly validate the set of signatures that cover these new fields,
-	// and ensure we're able to make upgrades to the network in a forwards
-	// compatible manner.
-	ExtraOpaqueData []byte
-
-	db kvdb.Backend
-}
-
-// AddNodeKeys is a setter-like method that can be used to replace the set of
-// keys for the target ChannelEdgeInfo.
-func (c *ChannelEdgeInfo) AddNodeKeys(nodeKey1, nodeKey2, bitcoinKey1,
-	bitcoinKey2 *btcec.PublicKey) {
-
-	c.nodeKey1 = nodeKey1
-	copy(c.NodeKey1Bytes[:], c.nodeKey1.SerializeCompressed())
-
-	c.nodeKey2 = nodeKey2
-	copy(c.NodeKey2Bytes[:], nodeKey2.SerializeCompressed())
-
-	c.bitcoinKey1 = bitcoinKey1
-	copy(c.BitcoinKey1Bytes[:], c.bitcoinKey1.SerializeCompressed())
-
-	c.bitcoinKey2 = bitcoinKey2
-	copy(c.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
-}
-
-// NodeKey1 is the identity public key of the "first" node that was involved in
-// the creation of this channel. A node is considered "first" if the
-// lexicographical ordering the its serialized public key is "smaller" than
-// that of the other node involved in channel creation.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the pubkey if absolutely necessary.
-func (c *ChannelEdgeInfo) NodeKey1() (*btcec.PublicKey, error) {
-	if c.nodeKey1 != nil {
-		return c.nodeKey1, nil
-	}
-
-	key, err := btcec.ParsePubKey(c.NodeKey1Bytes[:])
-	if err != nil {
-		return nil, err
-	}
-	c.nodeKey1 = key
-
-	return key, nil
-}
-
-// NodeKey2 is the identity public key of the "second" node that was
-// involved in the creation of this channel. A node is considered
-// "second" if the lexicographical ordering the its serialized public
-// key is "larger" than that of the other node involved in channel
-// creation.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the pubkey if absolutely necessary.
-func (c *ChannelEdgeInfo) NodeKey2() (*btcec.PublicKey, error) {
-	if c.nodeKey2 != nil {
-		return c.nodeKey2, nil
-	}
-
-	key, err := btcec.ParsePubKey(c.NodeKey2Bytes[:])
-	if err != nil {
-		return nil, err
-	}
-	c.nodeKey2 = key
-
-	return key, nil
-}
-
-// BitcoinKey1 is the Bitcoin multi-sig key belonging to the first
-// node, that was involved in the funding transaction that originally
-// created the channel that this struct represents.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the pubkey if absolutely necessary.
-func (c *ChannelEdgeInfo) BitcoinKey1() (*btcec.PublicKey, error) {
-	if c.bitcoinKey1 != nil {
-		return c.bitcoinKey1, nil
-	}
-
-	key, err := btcec.ParsePubKey(c.BitcoinKey1Bytes[:])
-	if err != nil {
-		return nil, err
-	}
-	c.bitcoinKey1 = key
-
-	return key, nil
-}
-
-// BitcoinKey2 is the Bitcoin multi-sig key belonging to the second
-// node, that was involved in the funding transaction that originally
-// created the channel that this struct represents.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the pubkey if absolutely necessary.
-func (c *ChannelEdgeInfo) BitcoinKey2() (*btcec.PublicKey, error) {
-	if c.bitcoinKey2 != nil {
-		return c.bitcoinKey2, nil
-	}
-
-	key, err := btcec.ParsePubKey(c.BitcoinKey2Bytes[:])
-	if err != nil {
-		return nil, err
-	}
-	c.bitcoinKey2 = key
-
-	return key, nil
-}
-
-// OtherNodeKeyBytes returns the node key bytes of the other end of
-// the channel.
-func (c *ChannelEdgeInfo) OtherNodeKeyBytes(thisNodeKey []byte) (
-	[33]byte, error) {
-
-	switch {
-	case bytes.Equal(c.NodeKey1Bytes[:], thisNodeKey):
-		return c.NodeKey2Bytes, nil
-	case bytes.Equal(c.NodeKey2Bytes[:], thisNodeKey):
-		return c.NodeKey1Bytes, nil
-	default:
-		return [33]byte{}, fmt.Errorf("node not participating in this channel")
-	}
+	return nodeTraversal(tx, nodePub[:], c.db, cb)
 }
 
 // FetchOtherNode attempts to fetch the full LightningNode that's opposite of
 // the target node in the channel. This is useful when one knows the pubkey of
 // one of the nodes, and wishes to obtain the full LightningNode for the other
 // end of the channel.
-func (c *ChannelEdgeInfo) FetchOtherNode(tx kvdb.RTx,
-	thisNodeKey []byte) (*LightningNode, error) {
+func (c *ChannelGraph) FetchOtherNode(tx kvdb.RTx,
+	channel *models.ChannelEdgeInfo, thisNodeKey []byte) (*LightningNode,
+	error) {
 
 	// Ensure that the node passed in is actually a member of the channel.
 	var targetNodeBytes [33]byte
 	switch {
-	case bytes.Equal(c.NodeKey1Bytes[:], thisNodeKey):
-		targetNodeBytes = c.NodeKey2Bytes
-	case bytes.Equal(c.NodeKey2Bytes[:], thisNodeKey):
-		targetNodeBytes = c.NodeKey1Bytes
+	case bytes.Equal(channel.NodeKey1Bytes[:], thisNodeKey):
+		targetNodeBytes = channel.NodeKey2Bytes
+	case bytes.Equal(channel.NodeKey2Bytes[:], thisNodeKey):
+		targetNodeBytes = channel.NodeKey1Bytes
 	default:
 		return nil, fmt.Errorf("node not participating in this channel")
 	}
@@ -3236,7 +3101,6 @@ func (c *ChannelEdgeInfo) FetchOtherNode(tx kvdb.RTx,
 		if err != nil {
 			return err
 		}
-		node.db = c.db
 
 		targetNode = &node
 
@@ -3255,271 +3119,19 @@ func (c *ChannelEdgeInfo) FetchOtherNode(tx kvdb.RTx,
 	return targetNode, err
 }
 
-// ChannelAuthProof is the authentication proof (the signature portion) for a
-// channel. Using the four signatures contained in the struct, and some
-// auxiliary knowledge (the funding script, node identities, and outpoint) nodes
-// on the network are able to validate the authenticity and existence of a
-// channel. Each of these signatures signs the following digest: chanID ||
-// nodeID1 || nodeID2 || bitcoinKey1|| bitcoinKey2 || 2-byte-feature-len ||
-// features.
-type ChannelAuthProof struct {
-	// nodeSig1 is a cached instance of the first node signature.
-	nodeSig1 *ecdsa.Signature
-
-	// NodeSig1Bytes are the raw bytes of the first node signature encoded
-	// in DER format.
-	NodeSig1Bytes []byte
-
-	// nodeSig2 is a cached instance of the second node signature.
-	nodeSig2 *ecdsa.Signature
-
-	// NodeSig2Bytes are the raw bytes of the second node signature
-	// encoded in DER format.
-	NodeSig2Bytes []byte
-
-	// bitcoinSig1 is a cached instance of the first bitcoin signature.
-	bitcoinSig1 *ecdsa.Signature
-
-	// BitcoinSig1Bytes are the raw bytes of the first bitcoin signature
-	// encoded in DER format.
-	BitcoinSig1Bytes []byte
-
-	// bitcoinSig2 is a cached instance of the second bitcoin signature.
-	bitcoinSig2 *ecdsa.Signature
-
-	// BitcoinSig2Bytes are the raw bytes of the second bitcoin signature
-	// encoded in DER format.
-	BitcoinSig2Bytes []byte
-}
-
-// Node1Sig is the signature using the identity key of the node that is first
-// in a lexicographical ordering of the serialized public keys of the two nodes
-// that created the channel.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the signature if absolutely necessary.
-func (c *ChannelAuthProof) Node1Sig() (*ecdsa.Signature, error) {
-	if c.nodeSig1 != nil {
-		return c.nodeSig1, nil
-	}
-
-	sig, err := ecdsa.ParseSignature(c.NodeSig1Bytes)
-	if err != nil {
-		return nil, err
-	}
-
-	c.nodeSig1 = sig
-
-	return sig, nil
-}
-
-// Node2Sig is the signature using the identity key of the node that is second
-// in a lexicographical ordering of the serialized public keys of the two nodes
-// that created the channel.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the signature if absolutely necessary.
-func (c *ChannelAuthProof) Node2Sig() (*ecdsa.Signature, error) {
-	if c.nodeSig2 != nil {
-		return c.nodeSig2, nil
-	}
-
-	sig, err := ecdsa.ParseSignature(c.NodeSig2Bytes)
-	if err != nil {
-		return nil, err
-	}
-
-	c.nodeSig2 = sig
-
-	return sig, nil
-}
-
-// BitcoinSig1 is the signature using the public key of the first node that was
-// used in the channel's multi-sig output.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the signature if absolutely necessary.
-func (c *ChannelAuthProof) BitcoinSig1() (*ecdsa.Signature, error) {
-	if c.bitcoinSig1 != nil {
-		return c.bitcoinSig1, nil
-	}
-
-	sig, err := ecdsa.ParseSignature(c.BitcoinSig1Bytes)
-	if err != nil {
-		return nil, err
-	}
-
-	c.bitcoinSig1 = sig
-
-	return sig, nil
-}
-
-// BitcoinSig2 is the signature using the public key of the second node that
-// was used in the channel's multi-sig output.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the signature if absolutely necessary.
-func (c *ChannelAuthProof) BitcoinSig2() (*ecdsa.Signature, error) {
-	if c.bitcoinSig2 != nil {
-		return c.bitcoinSig2, nil
-	}
-
-	sig, err := ecdsa.ParseSignature(c.BitcoinSig2Bytes)
-	if err != nil {
-		return nil, err
-	}
-
-	c.bitcoinSig2 = sig
-
-	return sig, nil
-}
-
-// IsEmpty check is the authentication proof is empty Proof is empty if at
-// least one of the signatures are equal to nil.
-func (c *ChannelAuthProof) IsEmpty() bool {
-	return len(c.NodeSig1Bytes) == 0 ||
-		len(c.NodeSig2Bytes) == 0 ||
-		len(c.BitcoinSig1Bytes) == 0 ||
-		len(c.BitcoinSig2Bytes) == 0
-}
-
-// ChannelEdgePolicy represents a *directed* edge within the channel graph. For
-// each channel in the database, there are two distinct edges: one for each
-// possible direction of travel along the channel. The edges themselves hold
-// information concerning fees, and minimum time-lock information which is
-// utilized during path finding.
-type ChannelEdgePolicy struct {
-	// SigBytes is the raw bytes of the signature of the channel edge
-	// policy. We'll only parse these if the caller needs to access the
-	// signature for validation purposes. Do not set SigBytes directly, but
-	// use SetSigBytes instead to make sure that the cache is invalidated.
-	SigBytes []byte
-
-	// sig is a cached fully parsed signature.
-	sig *ecdsa.Signature
-
-	// 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
-
-	// LastUpdate is the last time an authenticated edge for this channel
-	// was received.
-	LastUpdate time.Time
-
-	// MessageFlags is a bitfield which indicates the presence of optional
-	// fields (like max_htlc) in the policy.
-	MessageFlags lnwire.ChanUpdateMsgFlags
-
-	// ChannelFlags is a bitfield which signals the capabilities of the
-	// channel as well as the directed edge this update applies to.
-	ChannelFlags lnwire.ChanUpdateChanFlags
-
-	// TimeLockDelta is the number of blocks this node will subtract from
-	// the expiry of an incoming HTLC. This value expresses the time buffer
-	// the node would like to HTLC exchanges.
-	TimeLockDelta uint16
-
-	// MinHTLC is the smallest value HTLC this node will forward, expressed
-	// in millisatoshi.
-	MinHTLC lnwire.MilliSatoshi
-
-	// MaxHTLC is the largest value HTLC this node will forward, expressed
-	// in millisatoshi.
-	MaxHTLC lnwire.MilliSatoshi
-
-	// FeeBaseMSat is the base HTLC fee that will be charged for forwarding
-	// ANY HTLC, expressed in mSAT's.
-	FeeBaseMSat lnwire.MilliSatoshi
-
-	// FeeProportionalMillionths is the rate that the node will charge for
-	// HTLCs for each millionth of a satoshi forwarded.
-	FeeProportionalMillionths lnwire.MilliSatoshi
-
-	// Node is the LightningNode that this directed edge leads to. Using
-	// this pointer the channel graph can further be traversed.
-	Node *LightningNode
-
-	// ExtraOpaqueData is the set of data that was appended to this
-	// message, some of which we may not actually know how to iterate or
-	// parse. By holding onto this data, we ensure that we're able to
-	// properly validate the set of signatures that cover these new fields,
-	// and ensure we're able to make upgrades to the network in a forwards
-	// compatible manner.
-	ExtraOpaqueData []byte
-
-	db kvdb.Backend
-}
-
-// Signature is a channel announcement signature, which is needed for proper
-// edge policy announcement.
-//
-// NOTE: By having this method to access an attribute, we ensure we only need
-// to fully deserialize the signature if absolutely necessary.
-func (c *ChannelEdgePolicy) Signature() (*ecdsa.Signature, error) {
-	if c.sig != nil {
-		return c.sig, nil
-	}
-
-	sig, err := ecdsa.ParseSignature(c.SigBytes)
-	if err != nil {
-		return nil, err
-	}
-
-	c.sig = sig
-
-	return sig, nil
-}
-
-// SetSigBytes updates the signature and invalidates the cached parsed
-// signature.
-func (c *ChannelEdgePolicy) SetSigBytes(sig []byte) {
-	c.SigBytes = sig
-	c.sig = nil
-}
-
-// IsDisabled determines whether the edge has the disabled bit set.
-func (c *ChannelEdgePolicy) IsDisabled() bool {
-	return c.ChannelFlags.IsDisabled()
-}
-
-// ComputeFee computes the fee to forward an HTLC of `amt` milli-satoshis over
-// the passed active payment channel. This value is currently computed as
-// specified in BOLT07, but will likely change in the near future.
-func (c *ChannelEdgePolicy) ComputeFee(
-	amt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
-
-	return c.FeeBaseMSat + (amt*c.FeeProportionalMillionths)/feeRateParts
-}
-
-// divideCeil divides dividend by factor and rounds the result up.
-func divideCeil(dividend, factor lnwire.MilliSatoshi) lnwire.MilliSatoshi {
-	return (dividend + factor - 1) / factor
-}
-
-// ComputeFeeFromIncoming computes the fee to forward an HTLC given the incoming
-// amount.
-func (c *ChannelEdgePolicy) ComputeFeeFromIncoming(
-	incomingAmt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
-
-	return incomingAmt - divideCeil(
-		feeRateParts*(incomingAmt-c.FeeBaseMSat),
-		feeRateParts+c.FeeProportionalMillionths,
-	)
-}
-
 // FetchChannelEdgesByOutpoint attempts to lookup the two directed edges for
 // the channel identified by the funding outpoint. If the channel can't be
 // found, then ErrEdgeNotFound is returned. A struct which houses the general
 // information for the channel itself is returned as well as two structs that
 // contain the routing policies for the channel in either direction.
-func (c *ChannelGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint,
-) (*ChannelEdgeInfo, *ChannelEdgePolicy, *ChannelEdgePolicy, error) {
+func (c *ChannelGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (
+	*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+	*models.ChannelEdgePolicy, error) {
 
 	var (
-		edgeInfo *ChannelEdgeInfo
-		policy1  *ChannelEdgePolicy
-		policy2  *ChannelEdgePolicy
+		edgeInfo *models.ChannelEdgeInfo
+		policy1  *models.ChannelEdgePolicy
+		policy2  *models.ChannelEdgePolicy
 	)
 
 	err := kvdb.View(c.db, func(tx kvdb.RTx) error {
@@ -3564,14 +3176,11 @@ func (c *ChannelGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint,
 			return err
 		}
 		edgeInfo = &edge
-		edgeInfo.db = c.db
 
 		// Once we have the information about the channels' parameters,
 		// we'll fetch the routing policies for each for the directed
 		// edges.
-		e1, e2, err := fetchChanEdgePolicies(
-			edgeIndex, edges, nodes, chanID, c.db,
-		)
+		e1, e2, err := fetchChanEdgePolicies(edgeIndex, edges, chanID)
 		if err != nil {
 			return err
 		}
@@ -3600,13 +3209,14 @@ func (c *ChannelGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint,
 // ErrZombieEdge an be returned if the edge is currently marked as a zombie
 // within the database. In this case, the ChannelEdgePolicy's will be nil, and
 // the ChannelEdgeInfo will only include the public keys of each node.
-func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64,
-) (*ChannelEdgeInfo, *ChannelEdgePolicy, *ChannelEdgePolicy, error) {
+func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64) (
+	*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+	*models.ChannelEdgePolicy, error) {
 
 	var (
-		edgeInfo  *ChannelEdgeInfo
-		policy1   *ChannelEdgePolicy
-		policy2   *ChannelEdgePolicy
+		edgeInfo  *models.ChannelEdgeInfo
+		policy1   *models.ChannelEdgePolicy
+		policy2   *models.ChannelEdgePolicy
 		channelID [8]byte
 	)
 
@@ -3657,7 +3267,7 @@ func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64,
 			// populate the edge info with the public keys of each
 			// party as this is the only information we have about
 			// it and return an error signaling so.
-			edgeInfo = &ChannelEdgeInfo{
+			edgeInfo = &models.ChannelEdgeInfo{
 				NodeKey1Bytes: pubKey1,
 				NodeKey2Bytes: pubKey2,
 			}
@@ -3670,12 +3280,11 @@ func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64,
 		}
 
 		edgeInfo = &edge
-		edgeInfo.db = c.db
 
 		// Then we'll attempt to fetch the accompanying policies of this
 		// edge.
 		e1, e2, err := fetchChanEdgePolicies(
-			edgeIndex, edges, nodes, channelID[:], c.db,
+			edgeIndex, edges, channelID[:],
 		)
 		if err != nil {
 			return err
@@ -3718,7 +3327,7 @@ func (c *ChannelGraph) IsPublicNode(pubKey [33]byte) (bool, error) {
 			return err
 		}
 
-		nodeIsPublic, err = node.isPublic(tx, ourPubKey)
+		nodeIsPublic, err = c.isPublic(tx, node.PubKeyBytes, ourPubKey)
 		return err
 	}, func() {
 		nodeIsPublic = false
@@ -3834,11 +3443,6 @@ func (c *ChannelGraph) ChannelView() ([]EdgePoint, error) {
 	return edgePoints, nil
 }
 
-// NewChannelEdgePolicy returns a new blank ChannelEdgePolicy.
-func (c *ChannelGraph) NewChannelEdgePolicy() *ChannelEdgePolicy {
-	return &ChannelEdgePolicy{db: c.db}
-}
-
 // MarkEdgeZombie attempts to mark a channel identified by its channel ID as a
 // zombie. This method is used on an ad-hoc basis, when channels need to be
 // marked as zombies outside the normal pruning cycle.
@@ -4309,7 +3913,9 @@ func deserializeLightningNode(r io.Reader) (LightningNode, error) {
 	return node, nil
 }
 
-func putChanEdgeInfo(edgeIndex kvdb.RwBucket, edgeInfo *ChannelEdgeInfo, chanID [8]byte) error {
+func putChanEdgeInfo(edgeIndex kvdb.RwBucket,
+	edgeInfo *models.ChannelEdgeInfo, chanID [8]byte) error {
+
 	var b bytes.Buffer
 
 	if _, err := b.Write(edgeInfo.NodeKey1Bytes[:]); err != nil {
@@ -4376,58 +3982,58 @@ func putChanEdgeInfo(edgeIndex kvdb.RwBucket, edgeInfo *ChannelEdgeInfo, chanID
 }
 
 func fetchChanEdgeInfo(edgeIndex kvdb.RBucket,
-	chanID []byte) (ChannelEdgeInfo, error) {
+	chanID []byte) (models.ChannelEdgeInfo, error) {
 
 	edgeInfoBytes := edgeIndex.Get(chanID)
 	if edgeInfoBytes == nil {
-		return ChannelEdgeInfo{}, ErrEdgeNotFound
+		return models.ChannelEdgeInfo{}, ErrEdgeNotFound
 	}
 
 	edgeInfoReader := bytes.NewReader(edgeInfoBytes)
 	return deserializeChanEdgeInfo(edgeInfoReader)
 }
 
-func deserializeChanEdgeInfo(r io.Reader) (ChannelEdgeInfo, error) {
+func deserializeChanEdgeInfo(r io.Reader) (models.ChannelEdgeInfo, error) {
 	var (
 		err      error
-		edgeInfo ChannelEdgeInfo
+		edgeInfo models.ChannelEdgeInfo
 	)
 
 	if _, err := io.ReadFull(r, edgeInfo.NodeKey1Bytes[:]); err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 	if _, err := io.ReadFull(r, edgeInfo.NodeKey2Bytes[:]); err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 	if _, err := io.ReadFull(r, edgeInfo.BitcoinKey1Bytes[:]); err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 	if _, err := io.ReadFull(r, edgeInfo.BitcoinKey2Bytes[:]); err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 
 	edgeInfo.Features, err = wire.ReadVarBytes(r, 0, 900, "features")
 	if err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 
-	proof := &ChannelAuthProof{}
+	proof := &models.ChannelAuthProof{}
 
 	proof.NodeSig1Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs")
 	if err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 	proof.NodeSig2Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs")
 	if err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 	proof.BitcoinSig1Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs")
 	if err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 	proof.BitcoinSig2Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs")
 	if err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 
 	if !proof.IsEmpty() {
@@ -4436,17 +4042,17 @@ func deserializeChanEdgeInfo(r io.Reader) (ChannelEdgeInfo, error) {
 
 	edgeInfo.ChannelPoint = wire.OutPoint{}
 	if err := readOutpoint(r, &edgeInfo.ChannelPoint); err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 	if err := binary.Read(r, byteOrder, &edgeInfo.Capacity); err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 	if err := binary.Read(r, byteOrder, &edgeInfo.ChannelID); err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 
 	if _, err := io.ReadFull(r, edgeInfo.ChainHash[:]); err != nil {
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 
 	// We'll try and see if there are any opaque bytes left, if not, then
@@ -4458,13 +4064,13 @@ func deserializeChanEdgeInfo(r io.Reader) (ChannelEdgeInfo, error) {
 	case err == io.ErrUnexpectedEOF:
 	case err == io.EOF:
 	case err != nil:
-		return ChannelEdgeInfo{}, err
+		return models.ChannelEdgeInfo{}, err
 	}
 
 	return edgeInfo, nil
 }
 
-func putChanEdgePolicy(edges, nodes kvdb.RwBucket, edge *ChannelEdgePolicy,
+func putChanEdgePolicy(edges kvdb.RwBucket, edge *models.ChannelEdgePolicy,
 	from, to []byte) error {
 
 	var edgeKey [33 + 8]byte
@@ -4505,7 +4111,7 @@ func putChanEdgePolicy(edges, nodes kvdb.RwBucket, edge *ChannelEdgePolicy,
 		// TODO(halseth): get rid of these invalid policies in a
 		// migration.
 		oldEdgePolicy, err := deserializeChanEdgePolicy(
-			bytes.NewReader(edgeBytes), nodes,
+			bytes.NewReader(edgeBytes),
 		)
 		if err != nil && err != ErrEdgePolicyOptionalFieldNotFound {
 			return err
@@ -4585,7 +4191,7 @@ func putChanEdgePolicyUnknown(edges kvdb.RwBucket, channelID uint64,
 }
 
 func fetchChanEdgePolicy(edges kvdb.RBucket, chanID []byte,
-	nodePub []byte, nodes kvdb.RBucket) (*ChannelEdgePolicy, error) {
+	nodePub []byte) (*models.ChannelEdgePolicy, error) {
 
 	var edgeKey [33 + 8]byte
 	copy(edgeKey[:], nodePub)
@@ -4603,7 +4209,7 @@ func fetchChanEdgePolicy(edges kvdb.RBucket, chanID []byte,
 
 	edgeReader := bytes.NewReader(edgeBytes)
 
-	ep, err := deserializeChanEdgePolicy(edgeReader, nodes)
+	ep, err := deserializeChanEdgePolicy(edgeReader)
 	switch {
 	// If the db policy was missing an expected optional field, we return
 	// nil as if the policy was unknown.
@@ -4618,8 +4224,8 @@ func fetchChanEdgePolicy(edges kvdb.RBucket, chanID []byte,
 }
 
 func fetchChanEdgePolicies(edgeIndex kvdb.RBucket, edges kvdb.RBucket,
-	nodes kvdb.RBucket, chanID []byte,
-	db kvdb.Backend) (*ChannelEdgePolicy, *ChannelEdgePolicy, error) {
+	chanID []byte) (*models.ChannelEdgePolicy, *models.ChannelEdgePolicy,
+	error) {
 
 	edgeInfo := edgeIndex.Get(chanID)
 	if edgeInfo == nil {
@@ -4630,35 +4236,23 @@ func fetchChanEdgePolicies(edgeIndex kvdb.RBucket, edges kvdb.RBucket,
 	// information. We only propagate the error here and below if it's
 	// something other than edge non-existence.
 	node1Pub := edgeInfo[:33]
-	edge1, err := fetchChanEdgePolicy(edges, chanID, node1Pub, nodes)
+	edge1, err := fetchChanEdgePolicy(edges, chanID, node1Pub)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	// As we may have a single direction of the edge but not the other,
-	// only fill in the database pointers if the edge is found.
-	if edge1 != nil {
-		edge1.db = db
-		edge1.Node.db = db
-	}
-
 	// Similarly, the second node is contained within the latter
 	// half of the edge information.
 	node2Pub := edgeInfo[33:66]
-	edge2, err := fetchChanEdgePolicy(edges, chanID, node2Pub, nodes)
+	edge2, err := fetchChanEdgePolicy(edges, chanID, node2Pub)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	if edge2 != nil {
-		edge2.db = db
-		edge2.Node.db = db
-	}
-
 	return edge1, edge2, nil
 }
 
-func serializeChanEdgePolicy(w io.Writer, edge *ChannelEdgePolicy,
+func serializeChanEdgePolicy(w io.Writer, edge *models.ChannelEdgePolicy,
 	to []byte) error {
 
 	err := wire.WriteVarBytes(w, 0, edge.SigBytes)
@@ -4725,9 +4319,7 @@ func serializeChanEdgePolicy(w io.Writer, edge *ChannelEdgePolicy,
 	return nil
 }
 
-func deserializeChanEdgePolicy(r io.Reader,
-	nodes kvdb.RBucket) (*ChannelEdgePolicy, error) {
-
+func deserializeChanEdgePolicy(r io.Reader) (*models.ChannelEdgePolicy, error) {
 	// Deserialize the policy. Note that in case an optional field is not
 	// found, both an error and a populated policy object are returned.
 	edge, deserializeErr := deserializeChanEdgePolicyRaw(r)
@@ -4737,19 +4329,13 @@ func deserializeChanEdgePolicy(r io.Reader,
 		return nil, deserializeErr
 	}
 
-	// Populate full LightningNode struct.
-	pub := edge.Node.PubKeyBytes[:]
-	node, err := fetchLightningNode(nodes, pub)
-	if err != nil {
-		return nil, fmt.Errorf("unable to fetch node: %x, %v", pub, err)
-	}
-	edge.Node = &node
-
 	return edge, deserializeErr
 }
 
-func deserializeChanEdgePolicyRaw(r io.Reader) (*ChannelEdgePolicy, error) {
-	edge := &ChannelEdgePolicy{}
+func deserializeChanEdgePolicyRaw(r io.Reader) (*models.ChannelEdgePolicy,
+	error) {
+
+	edge := &models.ChannelEdgePolicy{}
 
 	var err error
 	edge.SigBytes, err = wire.ReadVarBytes(r, 0, 80, "sig")
@@ -4794,13 +4380,9 @@ func deserializeChanEdgePolicyRaw(r io.Reader) (*ChannelEdgePolicy, error) {
 	}
 	edge.FeeProportionalMillionths = lnwire.MilliSatoshi(n)
 
-	var pub [33]byte
-	if _, err := r.Read(pub[:]); err != nil {
+	if _, err := r.Read(edge.ToNode[:]); err != nil {
 		return nil, err
 	}
-	edge.Node = &LightningNode{
-		PubKeyBytes: pub,
-	}
 
 	// We'll try and see if there are any opaque bytes left, if not, then
 	// we'll ignore the EOF error and return the edge as is.
diff --git a/channeldb/graph_cache.go b/channeldb/graph_cache.go
index 1aae21d06..7bf9c41d9 100644
--- a/channeldb/graph_cache.go
+++ b/channeldb/graph_cache.go
@@ -5,6 +5,7 @@ import (
 	"sync"
 
 	"github.com/btcsuite/btcd/btcutil"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -27,94 +28,9 @@ type GraphCacheNode interface {
 	// error, then the iteration is halted with the error propagated back up
 	// to the caller.
 	ForEachChannel(kvdb.RTx,
-		func(kvdb.RTx, *ChannelEdgeInfo, *ChannelEdgePolicy,
-			*ChannelEdgePolicy) error) error
-}
-
-// CachedEdgePolicy is a struct that only caches the information of a
-// ChannelEdgePolicy that we actually use for pathfinding and therefore need to
-// store in the cache.
-type CachedEdgePolicy struct {
-	// 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
-
-	// MessageFlags is a bitfield which indicates the presence of optional
-	// fields (like max_htlc) in the policy.
-	MessageFlags lnwire.ChanUpdateMsgFlags
-
-	// ChannelFlags is a bitfield which signals the capabilities of the
-	// channel as well as the directed edge this update applies to.
-	ChannelFlags lnwire.ChanUpdateChanFlags
-
-	// TimeLockDelta is the number of blocks this node will subtract from
-	// the expiry of an incoming HTLC. This value expresses the time buffer
-	// the node would like to HTLC exchanges.
-	TimeLockDelta uint16
-
-	// MinHTLC is the smallest value HTLC this node will forward, expressed
-	// in millisatoshi.
-	MinHTLC lnwire.MilliSatoshi
-
-	// MaxHTLC is the largest value HTLC this node will forward, expressed
-	// in millisatoshi.
-	MaxHTLC lnwire.MilliSatoshi
-
-	// FeeBaseMSat is the base HTLC fee that will be charged for forwarding
-	// ANY HTLC, expressed in mSAT's.
-	FeeBaseMSat lnwire.MilliSatoshi
-
-	// FeeProportionalMillionths is the rate that the node will charge for
-	// HTLCs for each millionth of a satoshi forwarded.
-	FeeProportionalMillionths lnwire.MilliSatoshi
-
-	// ToNodePubKey is a function that returns the to node of a policy.
-	// Since we only ever store the inbound policy, this is always the node
-	// that we query the channels for in ForEachChannel(). Therefore, we can
-	// save a lot of space by not storing this information in the memory and
-	// instead just set this function when we copy the policy from cache in
-	// ForEachChannel().
-	ToNodePubKey func() route.Vertex
-
-	// ToNodeFeatures are the to node's features. They are never set while
-	// the edge is in the cache, only on the copy that is returned in
-	// ForEachChannel().
-	ToNodeFeatures *lnwire.FeatureVector
-}
-
-// ComputeFee computes the fee to forward an HTLC of `amt` milli-satoshis over
-// the passed active payment channel. This value is currently computed as
-// specified in BOLT07, but will likely change in the near future.
-func (c *CachedEdgePolicy) ComputeFee(
-	amt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
-
-	return c.FeeBaseMSat + (amt*c.FeeProportionalMillionths)/feeRateParts
-}
-
-// ComputeFeeFromIncoming computes the fee to forward an HTLC given the incoming
-// amount.
-func (c *CachedEdgePolicy) ComputeFeeFromIncoming(
-	incomingAmt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
-
-	return incomingAmt - divideCeil(
-		feeRateParts*(incomingAmt-c.FeeBaseMSat),
-		feeRateParts+c.FeeProportionalMillionths,
-	)
-}
-
-// NewCachedPolicy turns a full policy into a minimal one that can be cached.
-func NewCachedPolicy(policy *ChannelEdgePolicy) *CachedEdgePolicy {
-	return &CachedEdgePolicy{
-		ChannelID:                 policy.ChannelID,
-		MessageFlags:              policy.MessageFlags,
-		ChannelFlags:              policy.ChannelFlags,
-		TimeLockDelta:             policy.TimeLockDelta,
-		MinHTLC:                   policy.MinHTLC,
-		MaxHTLC:                   policy.MaxHTLC,
-		FeeBaseMSat:               policy.FeeBaseMSat,
-		FeeProportionalMillionths: policy.FeeProportionalMillionths,
-	}
+		func(kvdb.RTx, *models.ChannelEdgeInfo,
+			*models.ChannelEdgePolicy,
+			*models.ChannelEdgePolicy) error) error
 }
 
 // DirectedChannel is a type that stores the channel information as seen from
@@ -142,7 +58,7 @@ type DirectedChannel struct {
 	// In path finding, we're walking backward from the destination to the
 	// source, so we're always interested in the edge that arrives to us
 	// from the other node.
-	InPolicy *CachedEdgePolicy
+	InPolicy *models.CachedEdgePolicy
 }
 
 // DeepCopy creates a deep copy of the channel, including the incoming policy.
@@ -223,9 +139,9 @@ func (c *GraphCache) AddNode(tx kvdb.RTx, node GraphCacheNode) error {
 	c.AddNodeFeatures(node)
 
 	return node.ForEachChannel(
-		tx, func(tx kvdb.RTx, info *ChannelEdgeInfo,
-			outPolicy *ChannelEdgePolicy,
-			inPolicy *ChannelEdgePolicy) error {
+		tx, func(tx kvdb.RTx, info *models.ChannelEdgeInfo,
+			outPolicy *models.ChannelEdgePolicy,
+			inPolicy *models.ChannelEdgePolicy) error {
 
 			c.AddChannel(info, outPolicy, inPolicy)
 
@@ -238,8 +154,8 @@ func (c *GraphCache) AddNode(tx kvdb.RTx, node GraphCacheNode) error {
 // and policy 2 does not matter, the directionality is extracted from the info
 // and policy flags automatically. The policy will be set as the outgoing policy
 // on one node and the incoming policy on the peer's side.
-func (c *GraphCache) AddChannel(info *ChannelEdgeInfo,
-	policy1 *ChannelEdgePolicy, policy2 *ChannelEdgePolicy) {
+func (c *GraphCache) AddChannel(info *models.ChannelEdgeInfo,
+	policy1 *models.ChannelEdgePolicy, policy2 *models.ChannelEdgePolicy) {
 
 	if info == nil {
 		return
@@ -271,7 +187,7 @@ func (c *GraphCache) AddChannel(info *ChannelEdgeInfo,
 	// of node 2 then we have the policy 1 as seen from node 1.
 	if policy1 != nil {
 		fromNode, toNode := info.NodeKey1Bytes, info.NodeKey2Bytes
-		if policy1.Node.PubKeyBytes != info.NodeKey2Bytes {
+		if policy1.ToNode != info.NodeKey2Bytes {
 			fromNode, toNode = toNode, fromNode
 		}
 		isEdge1 := policy1.ChannelFlags&lnwire.ChanUpdateDirection == 0
@@ -279,7 +195,7 @@ func (c *GraphCache) AddChannel(info *ChannelEdgeInfo,
 	}
 	if policy2 != nil {
 		fromNode, toNode := info.NodeKey2Bytes, info.NodeKey1Bytes
-		if policy2.Node.PubKeyBytes != info.NodeKey1Bytes {
+		if policy2.ToNode != info.NodeKey1Bytes {
 			fromNode, toNode = toNode, fromNode
 		}
 		isEdge1 := policy2.ChannelFlags&lnwire.ChanUpdateDirection == 0
@@ -301,7 +217,7 @@ func (c *GraphCache) updateOrAddEdge(node route.Vertex, edge *DirectedChannel) {
 // of the from and to node is not strictly important. But we assume that a
 // channel edge was added beforehand so that the directed channel struct already
 // exists in the cache.
-func (c *GraphCache) UpdatePolicy(policy *ChannelEdgePolicy, fromNode,
+func (c *GraphCache) UpdatePolicy(policy *models.ChannelEdgePolicy, fromNode,
 	toNode route.Vertex, edge1 bool) {
 
 	c.mtx.Lock()
@@ -333,7 +249,7 @@ func (c *GraphCache) UpdatePolicy(policy *ChannelEdgePolicy, fromNode,
 		// The other two cases left mean it's the inbound policy for the
 		// node.
 		default:
-			channel.InPolicy = NewCachedPolicy(policy)
+			channel.InPolicy = models.NewCachedPolicy(policy)
 		}
 	}
 
@@ -380,7 +296,7 @@ func (c *GraphCache) removeChannelIfFound(node route.Vertex, chanID uint64) {
 // UpdateChannel updates the channel edge information for a specific edge. We
 // expect the edge to already exist and be known. If it does not yet exist, this
 // call is a no-op.
-func (c *GraphCache) UpdateChannel(info *ChannelEdgeInfo) {
+func (c *GraphCache) UpdateChannel(info *models.ChannelEdgeInfo) {
 	c.mtx.Lock()
 	defer c.mtx.Unlock()
 
diff --git a/channeldb/models/cached_edge_policy.go b/channeldb/models/cached_edge_policy.go
new file mode 100644
index 000000000..25bef567e
--- /dev/null
+++ b/channeldb/models/cached_edge_policy.go
@@ -0,0 +1,97 @@
+package models
+
+import (
+	"github.com/lightningnetwork/lnd/lnwire"
+	"github.com/lightningnetwork/lnd/routing/route"
+)
+
+const (
+	// feeRateParts is the total number of parts used to express fee rates.
+	feeRateParts = 1e6
+)
+
+// CachedEdgePolicy is a struct that only caches the information of a
+// ChannelEdgePolicy that we actually use for pathfinding and therefore need to
+// store in the cache.
+type CachedEdgePolicy struct {
+	// 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
+
+	// MessageFlags is a bitfield which indicates the presence of optional
+	// fields (like max_htlc) in the policy.
+	MessageFlags lnwire.ChanUpdateMsgFlags
+
+	// ChannelFlags is a bitfield which signals the capabilities of the
+	// channel as well as the directed edge this update applies to.
+	ChannelFlags lnwire.ChanUpdateChanFlags
+
+	// TimeLockDelta is the number of blocks this node will subtract from
+	// the expiry of an incoming HTLC. This value expresses the time buffer
+	// the node would like to HTLC exchanges.
+	TimeLockDelta uint16
+
+	// MinHTLC is the smallest value HTLC this node will forward, expressed
+	// in millisatoshi.
+	MinHTLC lnwire.MilliSatoshi
+
+	// MaxHTLC is the largest value HTLC this node will forward, expressed
+	// in millisatoshi.
+	MaxHTLC lnwire.MilliSatoshi
+
+	// FeeBaseMSat is the base HTLC fee that will be charged for forwarding
+	// ANY HTLC, expressed in mSAT's.
+	FeeBaseMSat lnwire.MilliSatoshi
+
+	// FeeProportionalMillionths is the rate that the node will charge for
+	// HTLCs for each millionth of a satoshi forwarded.
+	FeeProportionalMillionths lnwire.MilliSatoshi
+
+	// ToNodePubKey is a function that returns the to node of a policy.
+	// Since we only ever store the inbound policy, this is always the node
+	// that we query the channels for in ForEachChannel(). Therefore, we can
+	// save a lot of space by not storing this information in the memory and
+	// instead just set this function when we copy the policy from cache in
+	// ForEachChannel().
+	ToNodePubKey func() route.Vertex
+
+	// ToNodeFeatures are the to node's features. They are never set while
+	// the edge is in the cache, only on the copy that is returned in
+	// ForEachChannel().
+	ToNodeFeatures *lnwire.FeatureVector
+}
+
+// ComputeFee computes the fee to forward an HTLC of `amt` milli-satoshis over
+// the passed active payment channel. This value is currently computed as
+// specified in BOLT07, but will likely change in the near future.
+func (c *CachedEdgePolicy) ComputeFee(
+	amt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
+
+	return c.FeeBaseMSat + (amt*c.FeeProportionalMillionths)/feeRateParts
+}
+
+// ComputeFeeFromIncoming computes the fee to forward an HTLC given the incoming
+// amount.
+func (c *CachedEdgePolicy) ComputeFeeFromIncoming(
+	incomingAmt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
+
+	return incomingAmt - divideCeil(
+		feeRateParts*(incomingAmt-c.FeeBaseMSat),
+		feeRateParts+c.FeeProportionalMillionths,
+	)
+}
+
+// NewCachedPolicy turns a full policy into a minimal one that can be cached.
+func NewCachedPolicy(policy *ChannelEdgePolicy) *CachedEdgePolicy {
+	return &CachedEdgePolicy{
+		ChannelID:                 policy.ChannelID,
+		MessageFlags:              policy.MessageFlags,
+		ChannelFlags:              policy.ChannelFlags,
+		TimeLockDelta:             policy.TimeLockDelta,
+		MinHTLC:                   policy.MinHTLC,
+		MaxHTLC:                   policy.MaxHTLC,
+		FeeBaseMSat:               policy.FeeBaseMSat,
+		FeeProportionalMillionths: policy.FeeProportionalMillionths,
+	}
+}
diff --git a/channeldb/models/channel_auth_proof.go b/channeldb/models/channel_auth_proof.go
new file mode 100644
index 000000000..134139467
--- /dev/null
+++ b/channeldb/models/channel_auth_proof.go
@@ -0,0 +1,131 @@
+package models
+
+import "github.com/btcsuite/btcd/btcec/v2/ecdsa"
+
+// ChannelAuthProof is the authentication proof (the signature portion) for a
+// channel. Using the four signatures contained in the struct, and some
+// auxiliary knowledge (the funding script, node identities, and outpoint) nodes
+// on the network are able to validate the authenticity and existence of a
+// channel. Each of these signatures signs the following digest: chanID ||
+// nodeID1 || nodeID2 || bitcoinKey1|| bitcoinKey2 || 2-byte-feature-len ||
+// features.
+type ChannelAuthProof struct {
+	// nodeSig1 is a cached instance of the first node signature.
+	nodeSig1 *ecdsa.Signature
+
+	// NodeSig1Bytes are the raw bytes of the first node signature encoded
+	// in DER format.
+	NodeSig1Bytes []byte
+
+	// nodeSig2 is a cached instance of the second node signature.
+	nodeSig2 *ecdsa.Signature
+
+	// NodeSig2Bytes are the raw bytes of the second node signature
+	// encoded in DER format.
+	NodeSig2Bytes []byte
+
+	// bitcoinSig1 is a cached instance of the first bitcoin signature.
+	bitcoinSig1 *ecdsa.Signature
+
+	// BitcoinSig1Bytes are the raw bytes of the first bitcoin signature
+	// encoded in DER format.
+	BitcoinSig1Bytes []byte
+
+	// bitcoinSig2 is a cached instance of the second bitcoin signature.
+	bitcoinSig2 *ecdsa.Signature
+
+	// BitcoinSig2Bytes are the raw bytes of the second bitcoin signature
+	// encoded in DER format.
+	BitcoinSig2Bytes []byte
+}
+
+// Node1Sig is the signature using the identity key of the node that is first
+// in a lexicographical ordering of the serialized public keys of the two nodes
+// that created the channel.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the signature if absolutely necessary.
+func (c *ChannelAuthProof) Node1Sig() (*ecdsa.Signature, error) {
+	if c.nodeSig1 != nil {
+		return c.nodeSig1, nil
+	}
+
+	sig, err := ecdsa.ParseSignature(c.NodeSig1Bytes)
+	if err != nil {
+		return nil, err
+	}
+
+	c.nodeSig1 = sig
+
+	return sig, nil
+}
+
+// Node2Sig is the signature using the identity key of the node that is second
+// in a lexicographical ordering of the serialized public keys of the two nodes
+// that created the channel.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the signature if absolutely necessary.
+func (c *ChannelAuthProof) Node2Sig() (*ecdsa.Signature, error) {
+	if c.nodeSig2 != nil {
+		return c.nodeSig2, nil
+	}
+
+	sig, err := ecdsa.ParseSignature(c.NodeSig2Bytes)
+	if err != nil {
+		return nil, err
+	}
+
+	c.nodeSig2 = sig
+
+	return sig, nil
+}
+
+// BitcoinSig1 is the signature using the public key of the first node that was
+// used in the channel's multi-sig output.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the signature if absolutely necessary.
+func (c *ChannelAuthProof) BitcoinSig1() (*ecdsa.Signature, error) {
+	if c.bitcoinSig1 != nil {
+		return c.bitcoinSig1, nil
+	}
+
+	sig, err := ecdsa.ParseSignature(c.BitcoinSig1Bytes)
+	if err != nil {
+		return nil, err
+	}
+
+	c.bitcoinSig1 = sig
+
+	return sig, nil
+}
+
+// BitcoinSig2 is the signature using the public key of the second node that
+// was used in the channel's multi-sig output.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the signature if absolutely necessary.
+func (c *ChannelAuthProof) BitcoinSig2() (*ecdsa.Signature, error) {
+	if c.bitcoinSig2 != nil {
+		return c.bitcoinSig2, nil
+	}
+
+	sig, err := ecdsa.ParseSignature(c.BitcoinSig2Bytes)
+	if err != nil {
+		return nil, err
+	}
+
+	c.bitcoinSig2 = sig
+
+	return sig, nil
+}
+
+// IsEmpty check is the authentication proof is empty Proof is empty if at
+// least one of the signatures are equal to nil.
+func (c *ChannelAuthProof) IsEmpty() bool {
+	return len(c.NodeSig1Bytes) == 0 ||
+		len(c.NodeSig2Bytes) == 0 ||
+		len(c.BitcoinSig1Bytes) == 0 ||
+		len(c.BitcoinSig2Bytes) == 0
+}
diff --git a/channeldb/models/channel_edge_info.go b/channeldb/models/channel_edge_info.go
new file mode 100644
index 000000000..1afa2d627
--- /dev/null
+++ b/channeldb/models/channel_edge_info.go
@@ -0,0 +1,187 @@
+package models
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/btcsuite/btcd/btcec/v2"
+	"github.com/btcsuite/btcd/btcutil"
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+	"github.com/btcsuite/btcd/wire"
+)
+
+// ChannelEdgeInfo represents a fully authenticated channel along with all its
+// unique attributes. Once an authenticated channel announcement has been
+// processed on the network, then an instance of ChannelEdgeInfo encapsulating
+// the channels attributes is stored. The other portions relevant to routing
+// policy of a channel are stored within a ChannelEdgePolicy for each direction
+// of the channel.
+type ChannelEdgeInfo struct {
+	// 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
+
+	// ChainHash is the hash that uniquely identifies the chain that this
+	// channel was opened within.
+	//
+	// TODO(roasbeef): need to modify db keying for multi-chain
+	//  * must add chain hash to prefix as well
+	ChainHash chainhash.Hash
+
+	// NodeKey1Bytes is the raw public key of the first node.
+	NodeKey1Bytes [33]byte
+	nodeKey1      *btcec.PublicKey
+
+	// NodeKey2Bytes is the raw public key of the first node.
+	NodeKey2Bytes [33]byte
+	nodeKey2      *btcec.PublicKey
+
+	// BitcoinKey1Bytes is the raw public key of the first node.
+	BitcoinKey1Bytes [33]byte
+	bitcoinKey1      *btcec.PublicKey
+
+	// BitcoinKey2Bytes is the raw public key of the first node.
+	BitcoinKey2Bytes [33]byte
+	bitcoinKey2      *btcec.PublicKey
+
+	// Features is an opaque byte slice that encodes the set of channel
+	// specific features that this channel edge supports.
+	Features []byte
+
+	// AuthProof is the authentication proof for this channel. This proof
+	// contains a set of signatures binding four identities, which attests
+	// to the legitimacy of the advertised channel.
+	AuthProof *ChannelAuthProof
+
+	// ChannelPoint is the funding outpoint of the channel. This can be
+	// used to uniquely identify the channel within the channel graph.
+	ChannelPoint wire.OutPoint
+
+	// Capacity is the total capacity of the channel, this is determined by
+	// the value output in the outpoint that created this channel.
+	Capacity btcutil.Amount
+
+	// ExtraOpaqueData is the set of data that was appended to this
+	// message, some of which we may not actually know how to iterate or
+	// parse. By holding onto this data, we ensure that we're able to
+	// properly validate the set of signatures that cover these new fields,
+	// and ensure we're able to make upgrades to the network in a forwards
+	// compatible manner.
+	ExtraOpaqueData []byte
+}
+
+// AddNodeKeys is a setter-like method that can be used to replace the set of
+// keys for the target ChannelEdgeInfo.
+func (c *ChannelEdgeInfo) AddNodeKeys(nodeKey1, nodeKey2, bitcoinKey1,
+	bitcoinKey2 *btcec.PublicKey) {
+
+	c.nodeKey1 = nodeKey1
+	copy(c.NodeKey1Bytes[:], c.nodeKey1.SerializeCompressed())
+
+	c.nodeKey2 = nodeKey2
+	copy(c.NodeKey2Bytes[:], nodeKey2.SerializeCompressed())
+
+	c.bitcoinKey1 = bitcoinKey1
+	copy(c.BitcoinKey1Bytes[:], c.bitcoinKey1.SerializeCompressed())
+
+	c.bitcoinKey2 = bitcoinKey2
+	copy(c.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
+}
+
+// NodeKey1 is the identity public key of the "first" node that was involved in
+// the creation of this channel. A node is considered "first" if the
+// lexicographical ordering the its serialized public key is "smaller" than
+// that of the other node involved in channel creation.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the pubkey if absolutely necessary.
+func (c *ChannelEdgeInfo) NodeKey1() (*btcec.PublicKey, error) {
+	if c.nodeKey1 != nil {
+		return c.nodeKey1, nil
+	}
+
+	key, err := btcec.ParsePubKey(c.NodeKey1Bytes[:])
+	if err != nil {
+		return nil, err
+	}
+	c.nodeKey1 = key
+
+	return key, nil
+}
+
+// NodeKey2 is the identity public key of the "second" node that was involved in
+// the creation of this channel. A node is considered "second" if the
+// lexicographical ordering the its serialized public key is "larger" than that
+// of the other node involved in channel creation.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the pubkey if absolutely necessary.
+func (c *ChannelEdgeInfo) NodeKey2() (*btcec.PublicKey, error) {
+	if c.nodeKey2 != nil {
+		return c.nodeKey2, nil
+	}
+
+	key, err := btcec.ParsePubKey(c.NodeKey2Bytes[:])
+	if err != nil {
+		return nil, err
+	}
+	c.nodeKey2 = key
+
+	return key, nil
+}
+
+// BitcoinKey1 is the Bitcoin multi-sig key belonging to the first node, that
+// was involved in the funding transaction that originally created the channel
+// that this struct represents.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the pubkey if absolutely necessary.
+func (c *ChannelEdgeInfo) BitcoinKey1() (*btcec.PublicKey, error) {
+	if c.bitcoinKey1 != nil {
+		return c.bitcoinKey1, nil
+	}
+
+	key, err := btcec.ParsePubKey(c.BitcoinKey1Bytes[:])
+	if err != nil {
+		return nil, err
+	}
+	c.bitcoinKey1 = key
+
+	return key, nil
+}
+
+// BitcoinKey2 is the Bitcoin multi-sig key belonging to the second node, that
+// was involved in the funding transaction that originally created the channel
+// that this struct represents.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the pubkey if absolutely necessary.
+func (c *ChannelEdgeInfo) BitcoinKey2() (*btcec.PublicKey, error) {
+	if c.bitcoinKey2 != nil {
+		return c.bitcoinKey2, nil
+	}
+
+	key, err := btcec.ParsePubKey(c.BitcoinKey2Bytes[:])
+	if err != nil {
+		return nil, err
+	}
+	c.bitcoinKey2 = key
+
+	return key, nil
+}
+
+// OtherNodeKeyBytes returns the node key bytes of the other end of the channel.
+func (c *ChannelEdgeInfo) OtherNodeKeyBytes(thisNodeKey []byte) (
+	[33]byte, error) {
+
+	switch {
+	case bytes.Equal(c.NodeKey1Bytes[:], thisNodeKey):
+		return c.NodeKey2Bytes, nil
+	case bytes.Equal(c.NodeKey2Bytes[:], thisNodeKey):
+		return c.NodeKey1Bytes, nil
+	default:
+		return [33]byte{}, fmt.Errorf("node not participating in " +
+			"this channel")
+	}
+}
diff --git a/channeldb/models/channel_edge_policy.go b/channeldb/models/channel_edge_policy.go
new file mode 100644
index 000000000..50ad20c75
--- /dev/null
+++ b/channeldb/models/channel_edge_policy.go
@@ -0,0 +1,131 @@
+package models
+
+import (
+	"time"
+
+	"github.com/btcsuite/btcd/btcec/v2/ecdsa"
+	"github.com/lightningnetwork/lnd/lnwire"
+)
+
+// ChannelEdgePolicy represents a *directed* edge within the channel graph. For
+// each channel in the database, there are two distinct edges: one for each
+// possible direction of travel along the channel. The edges themselves hold
+// information concerning fees, and minimum time-lock information which is
+// utilized during path finding.
+type ChannelEdgePolicy struct {
+	// SigBytes is the raw bytes of the signature of the channel edge
+	// policy. We'll only parse these if the caller needs to access the
+	// signature for validation purposes. Do not set SigBytes directly, but
+	// use SetSigBytes instead to make sure that the cache is invalidated.
+	SigBytes []byte
+
+	// sig is a cached fully parsed signature.
+	sig *ecdsa.Signature
+
+	// 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
+
+	// LastUpdate is the last time an authenticated edge for this channel
+	// was received.
+	LastUpdate time.Time
+
+	// MessageFlags is a bitfield which indicates the presence of optional
+	// fields (like max_htlc) in the policy.
+	MessageFlags lnwire.ChanUpdateMsgFlags
+
+	// ChannelFlags is a bitfield which signals the capabilities of the
+	// channel as well as the directed edge this update applies to.
+	ChannelFlags lnwire.ChanUpdateChanFlags
+
+	// TimeLockDelta is the number of blocks this node will subtract from
+	// the expiry of an incoming HTLC. This value expresses the time buffer
+	// the node would like to HTLC exchanges.
+	TimeLockDelta uint16
+
+	// MinHTLC is the smallest value HTLC this node will forward, expressed
+	// in millisatoshi.
+	MinHTLC lnwire.MilliSatoshi
+
+	// MaxHTLC is the largest value HTLC this node will forward, expressed
+	// in millisatoshi.
+	MaxHTLC lnwire.MilliSatoshi
+
+	// FeeBaseMSat is the base HTLC fee that will be charged for forwarding
+	// ANY HTLC, expressed in mSAT's.
+	FeeBaseMSat lnwire.MilliSatoshi
+
+	// FeeProportionalMillionths is the rate that the node will charge for
+	// HTLCs for each millionth of a satoshi forwarded.
+	FeeProportionalMillionths lnwire.MilliSatoshi
+
+	// ToNode is the public key of the node that this directed edge leads
+	// to. Using this pub key, the channel graph can further be traversed.
+	ToNode [33]byte
+
+	// ExtraOpaqueData is the set of data that was appended to this
+	// message, some of which we may not actually know how to iterate or
+	// parse. By holding onto this data, we ensure that we're able to
+	// properly validate the set of signatures that cover these new fields,
+	// and ensure we're able to make upgrades to the network in a forwards
+	// compatible manner.
+	ExtraOpaqueData []byte
+}
+
+// Signature is a channel announcement signature, which is needed for proper
+// edge policy announcement.
+//
+// NOTE: By having this method to access an attribute, we ensure we only need
+// to fully deserialize the signature if absolutely necessary.
+func (c *ChannelEdgePolicy) Signature() (*ecdsa.Signature, error) {
+	if c.sig != nil {
+		return c.sig, nil
+	}
+
+	sig, err := ecdsa.ParseSignature(c.SigBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	c.sig = sig
+
+	return sig, nil
+}
+
+// SetSigBytes updates the signature and invalidates the cached parsed
+// signature.
+func (c *ChannelEdgePolicy) SetSigBytes(sig []byte) {
+	c.SigBytes = sig
+	c.sig = nil
+}
+
+// IsDisabled determines whether the edge has the disabled bit set.
+func (c *ChannelEdgePolicy) IsDisabled() bool {
+	return c.ChannelFlags.IsDisabled()
+}
+
+// ComputeFee computes the fee to forward an HTLC of `amt` milli-satoshis over
+// the passed active payment channel. This value is currently computed as
+// specified in BOLT07, but will likely change in the near future.
+func (c *ChannelEdgePolicy) ComputeFee(
+	amt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
+
+	return c.FeeBaseMSat + (amt*c.FeeProportionalMillionths)/feeRateParts
+}
+
+// divideCeil divides dividend by factor and rounds the result up.
+func divideCeil(dividend, factor lnwire.MilliSatoshi) lnwire.MilliSatoshi {
+	return (dividend + factor - 1) / factor
+}
+
+// ComputeFeeFromIncoming computes the fee to forward an HTLC given the incoming
+// amount.
+func (c *ChannelEdgePolicy) ComputeFeeFromIncoming(
+	incomingAmt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
+
+	return incomingAmt - divideCeil(
+		feeRateParts*(incomingAmt-c.FeeBaseMSat),
+		feeRateParts+c.FeeProportionalMillionths,
+	)
+}
diff --git a/discovery/chan_series.go b/discovery/chan_series.go
index 18f806316..c811017ab 100644
--- a/discovery/chan_series.go
+++ b/discovery/chan_series.go
@@ -280,10 +280,12 @@ func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
 
 			// If this edge has a validated node announcement, that
 			// we haven't yet sent, then we'll send that as well.
-			nodePub := channel.Policy1.Node.PubKeyBytes
-			hasNodeAnn := channel.Policy1.Node.HaveNodeAnnouncement
+			nodePub := channel.Node2.PubKeyBytes
+			hasNodeAnn := channel.Node2.HaveNodeAnnouncement
 			if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
-				nodeAnn, err := channel.Policy1.Node.NodeAnnouncement(true)
+				nodeAnn, err := channel.Node2.NodeAnnouncement(
+					true,
+				)
 				if err != nil {
 					return nil, err
 				}
@@ -297,10 +299,12 @@ func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
 
 			// If this edge has a validated node announcement, that
 			// we haven't yet sent, then we'll send that as well.
-			nodePub := channel.Policy2.Node.PubKeyBytes
-			hasNodeAnn := channel.Policy2.Node.HaveNodeAnnouncement
+			nodePub := channel.Node1.PubKeyBytes
+			hasNodeAnn := channel.Node1.HaveNodeAnnouncement
 			if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
-				nodeAnn, err := channel.Policy2.Node.NodeAnnouncement(true)
+				nodeAnn, err := channel.Node1.NodeAnnouncement(
+					true,
+				)
 				if err != nil {
 					return nil, err
 				}
diff --git a/discovery/gossiper.go b/discovery/gossiper.go
index eed3cee84..d2d2fb993 100644
--- a/discovery/gossiper.go
+++ b/discovery/gossiper.go
@@ -19,6 +19,7 @@ import (
 	"github.com/lightningnetwork/lnd/batch"
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnpeer"
@@ -527,10 +528,10 @@ func New(cfg Config, selfKeyDesc *keychain.KeyDescriptor) *AuthenticatedGossiper
 // EdgeWithInfo contains the information that is required to update an edge.
 type EdgeWithInfo struct {
 	// Info describes the channel.
-	Info *channeldb.ChannelEdgeInfo
+	Info *models.ChannelEdgeInfo
 
 	// Edge describes the policy in one direction of the channel.
-	Edge *channeldb.ChannelEdgePolicy
+	Edge *models.ChannelEdgePolicy
 }
 
 // PropagateChanPolicyUpdate signals the AuthenticatedGossiper to perform the
@@ -1574,8 +1575,8 @@ func (d *AuthenticatedGossiper) retransmitStaleAnns(now time.Time) error {
 	// Iterate over all of our channels and check if any of them fall
 	// within the prune interval or re-broadcast interval.
 	type updateTuple struct {
-		info *channeldb.ChannelEdgeInfo
-		edge *channeldb.ChannelEdgePolicy
+		info *models.ChannelEdgeInfo
+		edge *models.ChannelEdgePolicy
 	}
 
 	var (
@@ -1584,8 +1585,8 @@ func (d *AuthenticatedGossiper) retransmitStaleAnns(now time.Time) error {
 	)
 	err := d.cfg.Router.ForAllOutgoingChannels(func(
 		_ kvdb.RTx,
-		info *channeldb.ChannelEdgeInfo,
-		edge *channeldb.ChannelEdgePolicy) error {
+		info *models.ChannelEdgeInfo,
+		edge *models.ChannelEdgePolicy) error {
 
 		// If there's no auth proof attached to this edge, it means
 		// that it is a private channel not meant to be announced to
@@ -1790,7 +1791,7 @@ func (d *AuthenticatedGossiper) processChanPolicyUpdate(
 
 // remotePubFromChanInfo returns the public key of the remote peer given a
 // ChannelEdgeInfo that describe a channel we have with them.
-func remotePubFromChanInfo(chanInfo *channeldb.ChannelEdgeInfo,
+func remotePubFromChanInfo(chanInfo *models.ChannelEdgeInfo,
 	chanFlags lnwire.ChanUpdateChanFlags) [33]byte {
 
 	var remotePubKey [33]byte
@@ -1813,7 +1814,7 @@ func remotePubFromChanInfo(chanInfo *channeldb.ChannelEdgeInfo,
 // assemble the proof and craft the ChannelAnnouncement.
 func (d *AuthenticatedGossiper) processRejectedEdge(
 	chanAnnMsg *lnwire.ChannelAnnouncement,
-	proof *channeldb.ChannelAuthProof) ([]networkMsg, error) {
+	proof *models.ChannelAuthProof) ([]networkMsg, error) {
 
 	// First, we'll fetch the state of the channel as we know if from the
 	// database.
@@ -2022,7 +2023,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
 // processZombieUpdate determines whether the provided channel update should
 // resurrect a given zombie edge.
 func (d *AuthenticatedGossiper) processZombieUpdate(
-	chanInfo *channeldb.ChannelEdgeInfo, msg *lnwire.ChannelUpdate) error {
+	chanInfo *models.ChannelEdgeInfo, msg *lnwire.ChannelUpdate) error {
 
 	// The least-significant bit in the flag on the channel update tells us
 	// which edge is being updated.
@@ -2125,7 +2126,7 @@ func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool {
 		// Otherwise, we'll retrieve the correct policy that we
 		// currently have stored within our graph to check if this
 		// message is stale by comparing its timestamp.
-		var p *channeldb.ChannelEdgePolicy
+		var p *models.ChannelEdgePolicy
 		if msg.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
 			p = p1
 		} else {
@@ -2150,8 +2151,8 @@ func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool {
 
 // updateChannel creates a new fully signed update for the channel, and updates
 // the underlying graph with the new state.
-func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo,
-	edge *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement,
+func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo,
+	edge *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement,
 	*lnwire.ChannelUpdate, error) {
 
 	// Parse the unsigned edge into a channel update.
@@ -2239,7 +2240,7 @@ func (d *AuthenticatedGossiper) SyncManager() *SyncManager {
 // keep-alive update based on the previous channel update processed for the same
 // direction.
 func IsKeepAliveUpdate(update *lnwire.ChannelUpdate,
-	prev *channeldb.ChannelEdgePolicy) bool {
+	prev *models.ChannelEdgePolicy) bool {
 
 	// Both updates should be from the same direction.
 	if update.ChannelFlags&lnwire.ChanUpdateDirection !=
@@ -2428,7 +2429,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
 
 	// If this is a remote channel announcement, then we'll validate all
 	// the signatures within the proof as it should be well formed.
-	var proof *channeldb.ChannelAuthProof
+	var proof *models.ChannelAuthProof
 	if nMsg.isRemote {
 		if err := routing.ValidateChannelAnn(ann); err != nil {
 			err := fmt.Errorf("unable to validate announcement: "+
@@ -2448,7 +2449,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
 		// If the proof checks out, then we'll save the proof itself to
 		// the database so we can fetch it later when gossiping with
 		// other nodes.
-		proof = &channeldb.ChannelAuthProof{
+		proof = &models.ChannelAuthProof{
 			NodeSig1Bytes:    ann.NodeSig1.ToSignatureBytes(),
 			NodeSig2Bytes:    ann.NodeSig2.ToSignatureBytes(),
 			BitcoinSig1Bytes: ann.BitcoinSig1.ToSignatureBytes(),
@@ -2465,7 +2466,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
 		return nil, false
 	}
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:        ann.ShortChannelID.ToUint64(),
 		ChainHash:        ann.ChainHash,
 		NodeKey1Bytes:    ann.NodeID1,
@@ -2816,7 +2817,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
 	// being updated.
 	var (
 		pubKey       *btcec.PublicKey
-		edgeToUpdate *channeldb.ChannelEdgePolicy
+		edgeToUpdate *models.ChannelEdgePolicy
 	)
 	direction := upd.ChannelFlags & lnwire.ChanUpdateDirection
 	switch direction {
@@ -2905,7 +2906,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
 	// different alias. This might mean that SigBytes is incorrect as it
 	// signs a different SCID than the database SCID, but since there will
 	// only be a difference if AuthProof == nil, this is fine.
-	update := &channeldb.ChannelEdgePolicy{
+	update := &models.ChannelEdgePolicy{
 		SigBytes:                  upd.Signature.ToSignatureBytes(),
 		ChannelID:                 chanInfo.ChannelID,
 		LastUpdate:                timestamp,
@@ -3217,7 +3218,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg,
 	// We now have both halves of the channel announcement proof, then
 	// we'll reconstruct the initial announcement so we can validate it
 	// shortly below.
-	var dbProof channeldb.ChannelAuthProof
+	var dbProof models.ChannelAuthProof
 	if isFirstNode {
 		dbProof.NodeSig1Bytes = ann.NodeSignature.ToSignatureBytes()
 		dbProof.NodeSig2Bytes = oppProof.NodeSignature.ToSignatureBytes()
diff --git a/docs/release-notes/release-notes-0.18.0.md b/docs/release-notes/release-notes-0.18.0.md
index cdb79ed13..70095e724 100644
--- a/docs/release-notes/release-notes-0.18.0.md
+++ b/docs/release-notes/release-notes-0.18.0.md
@@ -104,6 +104,10 @@
   eliminate the use of `ScanInvoices`.
 
 ## Code Health
+
+* [Remove database pointers](https://github.com/lightningnetwork/lnd/pull/8117) 
+  from channeldb schema structs.
+
 ## Tooling and Documentation
 
 # Contributors (Alphabetical Order)
diff --git a/funding/manager.go b/funding/manager.go
index bee3f1481..f87db443d 100644
--- a/funding/manager.go
+++ b/funding/manager.go
@@ -524,7 +524,7 @@ type Config struct {
 	// DeleteAliasEdge allows the Manager to delete an alias channel edge
 	// from the graph. It also returns our local to-be-deleted policy.
 	DeleteAliasEdge func(scid lnwire.ShortChannelID) (
-		*channeldb.ChannelEdgePolicy, error)
+		*models.ChannelEdgePolicy, error)
 
 	// AliasManager is an implementation of the aliasHandler interface that
 	// abstracts away the handling of many alias functions.
@@ -3335,7 +3335,7 @@ func (f *Manager) extractAnnounceParams(c *channeldb.OpenChannel) (
 func (f *Manager) addToRouterGraph(completeChan *channeldb.OpenChannel,
 	shortChanID *lnwire.ShortChannelID,
 	peerAlias *lnwire.ShortChannelID,
-	ourPolicy *channeldb.ChannelEdgePolicy) error {
+	ourPolicy *models.ChannelEdgePolicy) error {
 
 	chanID := lnwire.NewChanIDFromOutPoint(&completeChan.FundingOutpoint)
 
@@ -4067,7 +4067,7 @@ func (f *Manager) newChanAnnouncement(localPubKey,
 	remotePubKey *btcec.PublicKey, localFundingKey *keychain.KeyDescriptor,
 	remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID,
 	chanID lnwire.ChannelID, fwdMinHTLC, fwdMaxHTLC lnwire.MilliSatoshi,
-	ourPolicy *channeldb.ChannelEdgePolicy,
+	ourPolicy *models.ChannelEdgePolicy,
 	chanType channeldb.ChannelType) (*chanAnnouncement, error) {
 
 	chainHash := *f.cfg.Wallet.Cfg.NetParams.GenesisHash
diff --git a/lnrpc/devrpc/dev_server.go b/lnrpc/devrpc/dev_server.go
index 462328ea3..afe30b361 100644
--- a/lnrpc/devrpc/dev_server.go
+++ b/lnrpc/devrpc/dev_server.go
@@ -17,6 +17,7 @@ import (
 	"github.com/btcsuite/btcd/wire"
 	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lncfg"
 	"github.com/lightningnetwork/lnd/lnrpc"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -261,7 +262,7 @@ func (s *Server) ImportGraph(ctx context.Context,
 	for _, rpcEdge := range graph.Edges {
 		rpcEdge := rpcEdge
 
-		edge := &channeldb.ChannelEdgeInfo{
+		edge := &models.ChannelEdgeInfo{
 			ChannelID: rpcEdge.ChannelId,
 			ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
 			Capacity:  btcutil.Amount(rpcEdge.Capacity),
@@ -288,8 +289,8 @@ func (s *Server) ImportGraph(ctx context.Context,
 				rpcEdge.ChanPoint, err)
 		}
 
-		makePolicy := func(rpcPolicy *lnrpc.RoutingPolicy) *channeldb.ChannelEdgePolicy {
-			policy := &channeldb.ChannelEdgePolicy{
+		makePolicy := func(rpcPolicy *lnrpc.RoutingPolicy) *models.ChannelEdgePolicy { //nolint:lll
+			policy := &models.ChannelEdgePolicy{
 				ChannelID: rpcEdge.ChannelId,
 				LastUpdate: time.Unix(
 					int64(rpcPolicy.LastUpdate), 0,
diff --git a/lnrpc/invoicesrpc/addinvoice.go b/lnrpc/invoicesrpc/addinvoice.go
index f300e4380..8b38b76a2 100644
--- a/lnrpc/invoicesrpc/addinvoice.go
+++ b/lnrpc/invoicesrpc/addinvoice.go
@@ -17,6 +17,7 @@ import (
 	"github.com/btcsuite/btcd/wire"
 	"github.com/davecgh/go-spew/spew"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/invoices"
 	"github.com/lightningnetwork/lnd/lntypes"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -489,7 +490,7 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
 // chanCanBeHopHint returns true if the target channel is eligible to be a hop
 // hint.
 func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) (
-	*channeldb.ChannelEdgePolicy, bool) {
+	*models.ChannelEdgePolicy, bool) {
 
 	// Since we're only interested in our private channels, we'll skip
 	// public ones.
@@ -544,7 +545,7 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) (
 
 	// Now, we'll need to determine which is the correct policy for HTLCs
 	// being sent from the remote node.
-	var remotePolicy *channeldb.ChannelEdgePolicy
+	var remotePolicy *models.ChannelEdgePolicy
 	if bytes.Equal(remotePub[:], info.NodeKey1Bytes[:]) {
 		remotePolicy = p1
 	} else {
@@ -604,7 +605,7 @@ func newHopHintInfo(c *channeldb.OpenChannel, isActive bool) *HopHintInfo {
 // newHopHint returns a new hop hint using the relevant data from a hopHintInfo
 // and a ChannelEdgePolicy.
 func newHopHint(hopHintInfo *HopHintInfo,
-	chanPolicy *channeldb.ChannelEdgePolicy) zpay32.HopHint {
+	chanPolicy *models.ChannelEdgePolicy) zpay32.HopHint {
 
 	return zpay32.HopHint{
 		NodeID:      hopHintInfo.RemotePubkey,
@@ -627,8 +628,8 @@ type SelectHopHintsCfg struct {
 
 	// FetchChannelEdgesByID attempts to lookup the two directed edges for
 	// the channel identified by the channel ID.
-	FetchChannelEdgesByID func(chanID uint64) (*channeldb.ChannelEdgeInfo,
-		*channeldb.ChannelEdgePolicy, *channeldb.ChannelEdgePolicy,
+	FetchChannelEdgesByID func(chanID uint64) (*models.ChannelEdgeInfo,
+		*models.ChannelEdgePolicy, *models.ChannelEdgePolicy,
 		error)
 
 	// GetAlias allows the peer's alias SCID to be retrieved for private
diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go
index feb3d4b85..549443f0f 100644
--- a/lnrpc/routerrpc/router_backend.go
+++ b/lnrpc/routerrpc/router_backend.go
@@ -15,6 +15,7 @@ import (
 	"github.com/btcsuite/btcd/wire"
 	sphinx "github.com/lightningnetwork/lightning-onion"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/feature"
 	"github.com/lightningnetwork/lnd/htlcswitch"
 	"github.com/lightningnetwork/lnd/lnrpc"
@@ -272,7 +273,7 @@ func (r *RouterBackend) parseQueryRoutesRequest(in *lnrpc.QueryRoutesRequest) (
 	// inside of the path rather than the request's fields.
 	var (
 		targetPubKey   *route.Vertex
-		routeHintEdges map[route.Vertex][]*channeldb.CachedEdgePolicy
+		routeHintEdges map[route.Vertex][]*models.CachedEdgePolicy
 		blindedPmt     *routing.BlindedPayment
 
 		// finalCLTVDelta varies depending on whether we're sending to
diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go
index 0ae8d606d..8fc040f6f 100644
--- a/netann/channel_announcement.go
+++ b/netann/channel_announcement.go
@@ -3,7 +3,7 @@ package netann
 import (
 	"bytes"
 
-	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 )
 
@@ -12,9 +12,9 @@ import (
 // function is used to transform out database structs into the corresponding wire
 // structs for announcing new channels to other peers, or simply syncing up a
 // peer's initial routing table upon connect.
-func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof,
-	chanInfo *channeldb.ChannelEdgeInfo,
-	e1, e2 *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement,
+func CreateChanAnnouncement(chanProof *models.ChannelAuthProof,
+	chanInfo *models.ChannelEdgeInfo,
+	e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement,
 	*lnwire.ChannelUpdate, *lnwire.ChannelUpdate, error) {
 
 	// First, using the parameters of the channel, along with the channel
diff --git a/netann/channel_update.go b/netann/channel_update.go
index b6555f37b..b93deb1d0 100644
--- a/netann/channel_update.go
+++ b/netann/channel_update.go
@@ -6,7 +6,7 @@ import (
 	"time"
 
 	"github.com/btcsuite/btcd/btcec/v2"
-	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -84,12 +84,12 @@ func SignChannelUpdate(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator
 //
 // NOTE: The passed policies can be nil.
 func ExtractChannelUpdate(ownerPubKey []byte,
-	info *channeldb.ChannelEdgeInfo,
-	policies ...*channeldb.ChannelEdgePolicy) (
+	info *models.ChannelEdgeInfo,
+	policies ...*models.ChannelEdgePolicy) (
 	*lnwire.ChannelUpdate, error) {
 
 	// Helper function to extract the owner of the given policy.
-	owner := func(edge *channeldb.ChannelEdgePolicy) []byte {
+	owner := func(edge *models.ChannelEdgePolicy) []byte {
 		var pubKey *btcec.PublicKey
 		if edge.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
 			pubKey, _ = info.NodeKey1()
@@ -117,8 +117,8 @@ func ExtractChannelUpdate(ownerPubKey []byte,
 
 // UnsignedChannelUpdateFromEdge reconstructs an unsigned ChannelUpdate from the
 // given edge info and policy.
-func UnsignedChannelUpdateFromEdge(info *channeldb.ChannelEdgeInfo,
-	policy *channeldb.ChannelEdgePolicy) *lnwire.ChannelUpdate {
+func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
+	policy *models.ChannelEdgePolicy) *lnwire.ChannelUpdate {
 
 	return &lnwire.ChannelUpdate{
 		ChainHash:       info.ChainHash,
@@ -137,8 +137,8 @@ func UnsignedChannelUpdateFromEdge(info *channeldb.ChannelEdgeInfo,
 
 // ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge
 // info and policy.
-func ChannelUpdateFromEdge(info *channeldb.ChannelEdgeInfo,
-	policy *channeldb.ChannelEdgePolicy) (*lnwire.ChannelUpdate, error) {
+func ChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
+	policy *models.ChannelEdgePolicy) (*lnwire.ChannelUpdate, error) {
 
 	update := UnsignedChannelUpdateFromEdge(info, policy)
 
diff --git a/netann/interface.go b/netann/interface.go
index 79c0c114f..d6cdb46d0 100644
--- a/netann/interface.go
+++ b/netann/interface.go
@@ -3,6 +3,7 @@ package netann
 import (
 	"github.com/btcsuite/btcd/wire"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 )
 
 // DB abstracts the required database functionality needed by the
@@ -18,6 +19,6 @@ type DB interface {
 type ChannelGraph interface {
 	// FetchChannelEdgesByOutpoint returns the channel edge info and most
 	// recent channel edge policies for a given outpoint.
-	FetchChannelEdgesByOutpoint(*wire.OutPoint) (*channeldb.ChannelEdgeInfo,
-		*channeldb.ChannelEdgePolicy, *channeldb.ChannelEdgePolicy, error)
+	FetchChannelEdgesByOutpoint(*wire.OutPoint) (*models.ChannelEdgeInfo,
+		*models.ChannelEdgePolicy, *models.ChannelEdgePolicy, error)
 }
diff --git a/peer/brontide.go b/peer/brontide.go
index 9453be39f..9600263e8 100644
--- a/peer/brontide.go
+++ b/peer/brontide.go
@@ -933,7 +933,7 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
 		//
 		// TODO(roasbeef): can add helper method to get policy for
 		// particular channel.
-		var selfPolicy *channeldb.ChannelEdgePolicy
+		var selfPolicy *models.ChannelEdgePolicy
 		if info != nil && bytes.Equal(info.NodeKey1Bytes[:],
 			p.cfg.ServerPubKey[:]) {
 
diff --git a/routing/blinding.go b/routing/blinding.go
index 143808646..50d3b6f79 100644
--- a/routing/blinding.go
+++ b/routing/blinding.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 
 	sphinx "github.com/lightningnetwork/lightning-onion"
-	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
 )
@@ -99,7 +99,7 @@ func (b *BlindedPayment) toRouteHints() RouteHints {
 
 	hintCount := len(b.BlindedPath.BlindedHops) - 1
 	hints := make(
-		map[route.Vertex][]*channeldb.CachedEdgePolicy, hintCount,
+		map[route.Vertex][]*models.CachedEdgePolicy, hintCount,
 	)
 
 	// Start at the unblinded introduction node, because our pathfinding
@@ -116,7 +116,7 @@ func (b *BlindedPayment) toRouteHints() RouteHints {
 	// will ensure that pathfinding provides sufficient fees/delay for the
 	// blinded portion to the introduction node.
 	firstBlindedHop := b.BlindedPath.BlindedHops[1].BlindedNodePub
-	hints[fromNode] = []*channeldb.CachedEdgePolicy{
+	hints[fromNode] = []*models.CachedEdgePolicy{
 		{
 			TimeLockDelta: b.CltvExpiryDelta,
 			MinHTLC:       lnwire.MilliSatoshi(b.HtlcMinimum),
@@ -156,14 +156,14 @@ func (b *BlindedPayment) toRouteHints() RouteHints {
 			b.BlindedPath.BlindedHops[nextHopIdx].BlindedNodePub,
 		)
 
-		hint := &channeldb.CachedEdgePolicy{
+		hint := &models.CachedEdgePolicy{
 			ToNodePubKey: func() route.Vertex {
 				return nextNode
 			},
 			ToNodeFeatures: features,
 		}
 
-		hints[fromNode] = []*channeldb.CachedEdgePolicy{
+		hints[fromNode] = []*models.CachedEdgePolicy{
 			hint,
 		}
 	}
diff --git a/routing/graph.go b/routing/graph.go
index 23bdd41e2..7f344f54a 100644
--- a/routing/graph.go
+++ b/routing/graph.go
@@ -75,7 +75,7 @@ func (g *CachedGraph) Close() error {
 func (g *CachedGraph) forEachNodeChannel(nodePub route.Vertex,
 	cb func(channel *channeldb.DirectedChannel) error) error {
 
-	return g.graph.ForEachNodeChannel(g.tx, nodePub, cb)
+	return g.graph.ForEachNodeDirectedChannel(g.tx, nodePub, cb)
 }
 
 // sourceNode returns the source node of the graph.
diff --git a/routing/heap.go b/routing/heap.go
index 36563bb66..261ced8f9 100644
--- a/routing/heap.go
+++ b/routing/heap.go
@@ -3,7 +3,7 @@ package routing
 import (
 	"container/heap"
 
-	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
 )
@@ -39,7 +39,7 @@ type nodeWithDist struct {
 	weight int64
 
 	// nextHop is the edge this route comes from.
-	nextHop *channeldb.CachedEdgePolicy
+	nextHop *models.CachedEdgePolicy
 
 	// routingInfoSize is the total size requirement for the payloads field
 	// in the onion packet from this hop towards the final destination.
diff --git a/routing/localchans/manager.go b/routing/localchans/manager.go
index 3a4e2416a..64641c739 100644
--- a/routing/localchans/manager.go
+++ b/routing/localchans/manager.go
@@ -31,8 +31,8 @@ type Manager struct {
 	// ForAllOutgoingChannels is required to iterate over all our local
 	// channels.
 	ForAllOutgoingChannels func(cb func(kvdb.RTx,
-		*channeldb.ChannelEdgeInfo,
-		*channeldb.ChannelEdgePolicy) error) error
+		*models.ChannelEdgeInfo,
+		*models.ChannelEdgePolicy) error) error
 
 	// FetchChannel is used to query local channel parameters. Optionally an
 	// existing db tx can be supplied.
@@ -73,8 +73,8 @@ func (r *Manager) UpdatePolicy(newSchema routing.ChannelPolicy,
 	// otherwise we'll collect them all.
 	err := r.ForAllOutgoingChannels(func(
 		tx kvdb.RTx,
-		info *channeldb.ChannelEdgeInfo,
-		edge *channeldb.ChannelEdgePolicy) error {
+		info *models.ChannelEdgeInfo,
+		edge *models.ChannelEdgePolicy) error {
 
 		// If we have a channel filter, and this channel isn't a part
 		// of it, then we'll skip it.
@@ -172,7 +172,7 @@ func (r *Manager) UpdatePolicy(newSchema routing.ChannelPolicy,
 
 // updateEdge updates the given edge with the new schema.
 func (r *Manager) updateEdge(tx kvdb.RTx, chanPoint wire.OutPoint,
-	edge *channeldb.ChannelEdgePolicy,
+	edge *models.ChannelEdgePolicy,
 	newSchema routing.ChannelPolicy) error {
 
 	// Update forwarding fee scheme and required time lock delta.
diff --git a/routing/notifications.go b/routing/notifications.go
index 387bd9126..9d6c4b51f 100644
--- a/routing/notifications.go
+++ b/routing/notifications.go
@@ -12,6 +12,7 @@ import (
 	"github.com/btcsuite/btcd/wire"
 	"github.com/go-errors/errors"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 )
 
@@ -212,7 +213,7 @@ type ClosedChanSummary struct {
 // createCloseSummaries takes in a slice of channels closed at the target block
 // height and creates a slice of summaries which of each channel closure.
 func createCloseSummaries(blockHeight uint32,
-	closedChans ...*channeldb.ChannelEdgeInfo) []*ClosedChanSummary {
+	closedChans ...*models.ChannelEdgeInfo) []*ClosedChanSummary {
 
 	closeSummaries := make([]*ClosedChanSummary, len(closedChans))
 	for i, closedChan := range closedChans {
@@ -333,12 +334,12 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange,
 
 	// We ignore initial channel announcements as we'll only send out
 	// updates once the individual edges themselves have been updated.
-	case *channeldb.ChannelEdgeInfo:
+	case *models.ChannelEdgeInfo:
 		return nil
 
 	// Any new ChannelUpdateAnnouncements will generate a corresponding
 	// ChannelEdgeUpdate notification.
-	case *channeldb.ChannelEdgePolicy:
+	case *models.ChannelEdgePolicy:
 		// We'll need to fetch the edge's information from the database
 		// in order to get the information concerning which nodes are
 		// being connected.
diff --git a/routing/pathfind.go b/routing/pathfind.go
index d007be05e..091c18d92 100644
--- a/routing/pathfind.go
+++ b/routing/pathfind.go
@@ -11,6 +11,7 @@ import (
 	"github.com/btcsuite/btcd/btcutil"
 	sphinx "github.com/lightningnetwork/lightning-onion"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/feature"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/record"
@@ -49,7 +50,7 @@ const (
 type pathFinder = func(g *graphParams, r *RestrictParams,
 	cfg *PathFindingConfig, source, target route.Vertex,
 	amt lnwire.MilliSatoshi, timePref float64, finalHtlcExpiry int32) (
-	[]*channeldb.CachedEdgePolicy, float64, error)
+	[]*models.CachedEdgePolicy, float64, error)
 
 var (
 	// DefaultEstimator is the default estimator used for computing
@@ -87,7 +88,7 @@ var (
 // of the edge.
 type edgePolicyWithSource struct {
 	sourceNode route.Vertex
-	edge       *channeldb.CachedEdgePolicy
+	edge       *models.CachedEdgePolicy
 }
 
 // finalHopParams encapsulates various parameters for route construction that
@@ -125,7 +126,7 @@ type finalHopParams struct {
 // NOTE: If a non-nil blinded path is provided it is assumed to have been
 // validated by the caller.
 func newRoute(sourceVertex route.Vertex,
-	pathEdges []*channeldb.CachedEdgePolicy, currentHeight uint32,
+	pathEdges []*models.CachedEdgePolicy, currentHeight uint32,
 	finalHop finalHopParams, blindedPath *sphinx.BlindedPath) (
 	*route.Route, error) {
 
@@ -355,7 +356,7 @@ type graphParams struct {
 	// additionalEdges is an optional set of edges that should be
 	// considered during path finding, that is not already found in the
 	// channel graph.
-	additionalEdges map[route.Vertex][]*channeldb.CachedEdgePolicy
+	additionalEdges map[route.Vertex][]*models.CachedEdgePolicy
 
 	// bandwidthHints is an interface that provides bandwidth hints that
 	// can provide a better estimate of the current channel bandwidth than
@@ -493,7 +494,7 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
 // available bandwidth.
 func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
 	source, target route.Vertex, amt lnwire.MilliSatoshi, timePref float64,
-	finalHtlcExpiry int32) ([]*channeldb.CachedEdgePolicy, float64, error) {
+	finalHtlcExpiry int32) ([]*models.CachedEdgePolicy, float64, error) {
 
 	// Pathfinding can be a significant portion of the total payment
 	// latency, especially on low-powered devices. Log several metrics to
@@ -994,7 +995,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
 
 	// Use the distance map to unravel the forward path from source to
 	// target.
-	var pathEdges []*channeldb.CachedEdgePolicy
+	var pathEdges []*models.CachedEdgePolicy
 	currentNode := source
 	for {
 		// Determine the next hop forward using the next map.
diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go
index d84c264c2..b2c3834f2 100644
--- a/routing/payment_lifecycle.go
+++ b/routing/payment_lifecycle.go
@@ -9,6 +9,7 @@ import (
 	"github.com/go-errors/errors"
 	sphinx "github.com/lightningnetwork/lightning-onion"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/htlcswitch"
 	"github.com/lightningnetwork/lnd/lntypes"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -874,7 +875,7 @@ func (p *shardHandler) handleFailureMessage(rt *route.Route,
 
 	var (
 		isAdditionalEdge bool
-		policy           *channeldb.CachedEdgePolicy
+		policy           *models.CachedEdgePolicy
 	)
 
 	// Before we apply the channel update, we need to decide whether the
diff --git a/routing/payment_session.go b/routing/payment_session.go
index 0a9078502..a04a4de55 100644
--- a/routing/payment_session.go
+++ b/routing/payment_session.go
@@ -8,6 +8,7 @@ import (
 	"github.com/davecgh/go-spew/spew"
 	"github.com/lightningnetwork/lnd/build"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
 )
@@ -144,13 +145,13 @@ type PaymentSession interface {
 	// a boolean to indicate whether the update has been applied without
 	// error.
 	UpdateAdditionalEdge(msg *lnwire.ChannelUpdate, pubKey *btcec.PublicKey,
-		policy *channeldb.CachedEdgePolicy) bool
+		policy *models.CachedEdgePolicy) bool
 
 	// GetAdditionalEdgePolicy uses the public key and channel ID to query
 	// the ephemeral channel edge policy for additional edges. Returns a nil
 	// if nothing found.
 	GetAdditionalEdgePolicy(pubKey *btcec.PublicKey,
-		channelID uint64) *channeldb.CachedEdgePolicy
+		channelID uint64) *models.CachedEdgePolicy
 }
 
 // paymentSession is used during an HTLC routings session to prune the local
@@ -162,7 +163,7 @@ type PaymentSession interface {
 // loop if payment attempts take long enough. An additional set of edges can
 // also be provided to assist in reaching the payment's destination.
 type paymentSession struct {
-	additionalEdges map[route.Vertex][]*channeldb.CachedEdgePolicy
+	additionalEdges map[route.Vertex][]*models.CachedEdgePolicy
 
 	getBandwidthHints func(routingGraph) (bandwidthHints, error)
 
@@ -405,7 +406,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
 // updates to the supplied policy. It returns a boolean to indicate whether
 // there's an error when applying the updates.
 func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
-	pubKey *btcec.PublicKey, policy *channeldb.CachedEdgePolicy) bool {
+	pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool {
 
 	// Validate the message signature.
 	if err := VerifyChannelUpdateSignature(msg, pubKey); err != nil {
@@ -430,7 +431,7 @@ func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
 // ephemeral channel edge policy for additional edges. Returns a nil if nothing
 // found.
 func (p *paymentSession) GetAdditionalEdgePolicy(pubKey *btcec.PublicKey,
-	channelID uint64) *channeldb.CachedEdgePolicy {
+	channelID uint64) *models.CachedEdgePolicy {
 
 	target := route.NewVertex(pubKey)
 
diff --git a/routing/payment_session_source.go b/routing/payment_session_source.go
index 9527e4e13..229c932f3 100644
--- a/routing/payment_session_source.go
+++ b/routing/payment_session_source.go
@@ -3,6 +3,7 @@ package routing
 import (
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
 	"github.com/lightningnetwork/lnd/zpay32"
@@ -94,9 +95,9 @@ func (m *SessionSource) NewPaymentSessionEmpty() PaymentSession {
 // RouteHintsToEdges converts a list of invoice route hints to an edge map that
 // can be passed into pathfinding.
 func RouteHintsToEdges(routeHints [][]zpay32.HopHint, target route.Vertex) (
-	map[route.Vertex][]*channeldb.CachedEdgePolicy, error) {
+	map[route.Vertex][]*models.CachedEdgePolicy, error) {
 
-	edges := make(map[route.Vertex][]*channeldb.CachedEdgePolicy)
+	edges := make(map[route.Vertex][]*models.CachedEdgePolicy)
 
 	// Traverse through all of the available hop hints and include them in
 	// our edges map, indexed by the public key of the channel's starting
@@ -126,7 +127,7 @@ func RouteHintsToEdges(routeHints [][]zpay32.HopHint, target route.Vertex) (
 			// Finally, create the channel edge from the hop hint
 			// and add it to list of edges corresponding to the node
 			// at the start of the channel.
-			edge := &channeldb.CachedEdgePolicy{
+			edge := &models.CachedEdgePolicy{
 				ToNodePubKey: func() route.Vertex {
 					return endNode.PubKeyBytes
 				},
diff --git a/routing/router.go b/routing/router.go
index 827e21864..fa255544c 100644
--- a/routing/router.go
+++ b/routing/router.go
@@ -21,6 +21,7 @@ import (
 	"github.com/lightningnetwork/lnd/batch"
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/clock"
 	"github.com/lightningnetwork/lnd/htlcswitch"
 	"github.com/lightningnetwork/lnd/input"
@@ -135,17 +136,17 @@ type ChannelGraphSource interface {
 	// AddEdge is used to add edge/channel to the topology of the router,
 	// after all information about channel will be gathered this
 	// edge/channel might be used in construction of payment path.
-	AddEdge(edge *channeldb.ChannelEdgeInfo,
+	AddEdge(edge *models.ChannelEdgeInfo,
 		op ...batch.SchedulerOption) error
 
 	// AddProof updates the channel edge info with proof which is needed to
 	// properly announce the edge to the rest of the network.
 	AddProof(chanID lnwire.ShortChannelID,
-		proof *channeldb.ChannelAuthProof) error
+		proof *models.ChannelAuthProof) error
 
 	// UpdateEdge is used to update edge information, without this message
 	// edge considered as not fully constructed.
-	UpdateEdge(policy *channeldb.ChannelEdgePolicy,
+	UpdateEdge(policy *models.ChannelEdgePolicy,
 		op ...batch.SchedulerOption) error
 
 	// IsStaleNode returns true if the graph source has a node announcement
@@ -176,8 +177,8 @@ type ChannelGraphSource interface {
 	// emanating from the "source" node which is the center of the
 	// star-graph.
 	ForAllOutgoingChannels(cb func(tx kvdb.RTx,
-		c *channeldb.ChannelEdgeInfo,
-		e *channeldb.ChannelEdgePolicy) error) error
+		c *models.ChannelEdgeInfo,
+		e *models.ChannelEdgePolicy) error) error
 
 	// CurrentBlockHeight returns the block height from POV of the router
 	// subsystem.
@@ -185,8 +186,8 @@ type ChannelGraphSource interface {
 
 	// GetChannelByID return the channel by the channel id.
 	GetChannelByID(chanID lnwire.ShortChannelID) (
-		*channeldb.ChannelEdgeInfo, *channeldb.ChannelEdgePolicy,
-		*channeldb.ChannelEdgePolicy, error)
+		*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+		*models.ChannelEdgePolicy, error)
 
 	// FetchLightningNode attempts to look up a target node by its identity
 	// public key. channeldb.ErrGraphNodeNotFound is returned if the node
@@ -897,15 +898,15 @@ func (r *ChannelRouter) pruneZombieChans() error {
 	log.Infof("Examining channel graph for zombie channels")
 
 	// A helper method to detect if the channel belongs to this node
-	isSelfChannelEdge := func(info *channeldb.ChannelEdgeInfo) bool {
+	isSelfChannelEdge := func(info *models.ChannelEdgeInfo) bool {
 		return info.NodeKey1Bytes == r.selfNode.PubKeyBytes ||
 			info.NodeKey2Bytes == r.selfNode.PubKeyBytes
 	}
 
 	// First, we'll collect all the channels which are eligible for garbage
 	// collection due to being zombies.
-	filterPruneChans := func(info *channeldb.ChannelEdgeInfo,
-		e1, e2 *channeldb.ChannelEdgePolicy) error {
+	filterPruneChans := func(info *models.ChannelEdgeInfo,
+		e1, e2 *models.ChannelEdgePolicy) error {
 
 		// Exit early in case this channel is already marked to be pruned
 		if _, markedToPrune := chansToPrune[info.ChannelID]; markedToPrune {
@@ -1539,7 +1540,7 @@ func (r *ChannelRouter) processUpdate(msg interface{},
 		log.Tracef("Updated vertex data for node=%x", msg.PubKeyBytes)
 		r.stats.incNumNodeUpdates()
 
-	case *channeldb.ChannelEdgeInfo:
+	case *models.ChannelEdgeInfo:
 		log.Debugf("Received ChannelEdgeInfo for channel %v",
 			msg.ChannelID)
 
@@ -1706,7 +1707,7 @@ func (r *ChannelRouter) processUpdate(msg interface{},
 				"view: %v", err)
 		}
 
-	case *channeldb.ChannelEdgePolicy:
+	case *models.ChannelEdgePolicy:
 		log.Debugf("Received ChannelEdgePolicy for channel %v",
 			msg.ChannelID)
 
@@ -1883,7 +1884,7 @@ type RouteRequest struct {
 
 // RouteHints is an alias type for a set of route hints, with the source node
 // as the map's key and the details of the hint(s) in the edge policy.
-type RouteHints map[route.Vertex][]*channeldb.CachedEdgePolicy
+type RouteHints map[route.Vertex][]*models.CachedEdgePolicy
 
 // NewRouteRequest produces a new route request for a regular payment or one
 // to a blinded route, validating that the target, routeHints and finalExpiry
@@ -2668,7 +2669,7 @@ func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate) bool {
 		return false
 	}
 
-	err = r.UpdateEdge(&channeldb.ChannelEdgePolicy{
+	err = r.UpdateEdge(&models.ChannelEdgePolicy{
 		SigBytes:                  msg.Signature.ToSignatureBytes(),
 		ChannelID:                 msg.ShortChannelID.ToUint64(),
 		LastUpdate:                time.Unix(int64(msg.Timestamp), 0),
@@ -2720,7 +2721,7 @@ func (r *ChannelRouter) AddNode(node *channeldb.LightningNode,
 // in construction of payment path.
 //
 // NOTE: This method is part of the ChannelGraphSource interface.
-func (r *ChannelRouter) AddEdge(edge *channeldb.ChannelEdgeInfo,
+func (r *ChannelRouter) AddEdge(edge *models.ChannelEdgeInfo,
 	op ...batch.SchedulerOption) error {
 
 	rMsg := &routingMsg{
@@ -2746,7 +2747,7 @@ func (r *ChannelRouter) AddEdge(edge *channeldb.ChannelEdgeInfo,
 // considered as not fully constructed.
 //
 // NOTE: This method is part of the ChannelGraphSource interface.
-func (r *ChannelRouter) UpdateEdge(update *channeldb.ChannelEdgePolicy,
+func (r *ChannelRouter) UpdateEdge(update *models.ChannelEdgePolicy,
 	op ...batch.SchedulerOption) error {
 
 	rMsg := &routingMsg{
@@ -2787,9 +2788,9 @@ func (r *ChannelRouter) SyncedHeight() uint32 {
 //
 // NOTE: This method is part of the ChannelGraphSource interface.
 func (r *ChannelRouter) GetChannelByID(chanID lnwire.ShortChannelID) (
-	*channeldb.ChannelEdgeInfo,
-	*channeldb.ChannelEdgePolicy,
-	*channeldb.ChannelEdgePolicy, error) {
+	*models.ChannelEdgeInfo,
+	*models.ChannelEdgePolicy,
+	*models.ChannelEdgePolicy, error) {
 
 	return r.cfg.Graph.FetchChannelEdgesByID(chanID.ToUint64())
 }
@@ -2802,7 +2803,7 @@ func (r *ChannelRouter) GetChannelByID(chanID lnwire.ShortChannelID) (
 func (r *ChannelRouter) FetchLightningNode(
 	node route.Vertex) (*channeldb.LightningNode, error) {
 
-	return r.cfg.Graph.FetchLightningNode(node)
+	return r.cfg.Graph.FetchLightningNode(nil, node)
 }
 
 // ForEachNode is used to iterate over every node in router topology.
@@ -2822,18 +2823,21 @@ func (r *ChannelRouter) ForEachNode(
 //
 // NOTE: This method is part of the ChannelGraphSource interface.
 func (r *ChannelRouter) ForAllOutgoingChannels(cb func(kvdb.RTx,
-	*channeldb.ChannelEdgeInfo, *channeldb.ChannelEdgePolicy) error) error {
+	*models.ChannelEdgeInfo, *models.ChannelEdgePolicy) error) error {
 
-	return r.selfNode.ForEachChannel(nil, func(tx kvdb.RTx,
-		c *channeldb.ChannelEdgeInfo,
-		e, _ *channeldb.ChannelEdgePolicy) error {
+	return r.cfg.Graph.ForEachNodeChannel(nil, r.selfNode.PubKeyBytes,
+		func(tx kvdb.RTx, c *models.ChannelEdgeInfo,
+			e *models.ChannelEdgePolicy,
+			_ *models.ChannelEdgePolicy) error {
 
-		if e == nil {
-			return fmt.Errorf("channel from self node has no policy")
-		}
+			if e == nil {
+				return fmt.Errorf("channel from self node " +
+					"has no policy")
+			}
 
-		return cb(tx, c, e)
-	})
+			return cb(tx, c, e)
+		},
+	)
 }
 
 // AddProof updates the channel edge info with proof which is needed to
@@ -2841,7 +2845,7 @@ func (r *ChannelRouter) ForAllOutgoingChannels(cb func(kvdb.RTx,
 //
 // NOTE: This method is part of the ChannelGraphSource interface.
 func (r *ChannelRouter) AddProof(chanID lnwire.ShortChannelID,
-	proof *channeldb.ChannelAuthProof) error {
+	proof *models.ChannelAuthProof) error {
 
 	info, _, _, err := r.cfg.Graph.FetchChannelEdgesByID(chanID.ToUint64())
 	if err != nil {
@@ -3126,14 +3130,14 @@ func getRouteUnifiers(source route.Vertex, hops []route.Vertex,
 // including fees, to send the payment.
 func getPathEdges(source route.Vertex, receiverAmt lnwire.MilliSatoshi,
 	unifiers []*edgeUnifier, bandwidthHints *bandwidthManager,
-	hops []route.Vertex) ([]*channeldb.CachedEdgePolicy,
+	hops []route.Vertex) ([]*models.CachedEdgePolicy,
 	lnwire.MilliSatoshi, error) {
 
 	// Now that we arrived at the start of the route and found out the route
 	// total amount, we make a forward pass. Because the amount may have
 	// been increased in the backward pass, fees need to be recalculated and
 	// amount ranges re-checked.
-	var pathEdges []*channeldb.CachedEdgePolicy
+	var pathEdges []*models.CachedEdgePolicy
 	for i, unifier := range unifiers {
 		edge := unifier.getEdge(receiverAmt, bandwidthHints)
 		if edge == nil {
diff --git a/routing/unified_edges.go b/routing/unified_edges.go
index e8c2a9270..aee168348 100644
--- a/routing/unified_edges.go
+++ b/routing/unified_edges.go
@@ -3,6 +3,7 @@ package routing
 import (
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lntypes"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -41,7 +42,7 @@ func newNodeEdgeUnifier(sourceNode, toNode route.Vertex,
 // addPolicy adds a single channel policy. Capacity may be zero if unknown
 // (light clients).
 func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
-	edge *channeldb.CachedEdgePolicy, capacity btcutil.Amount) {
+	edge *models.CachedEdgePolicy, capacity btcutil.Amount) {
 
 	localChan := fromNode == u.sourceNode
 
@@ -93,7 +94,7 @@ func (u *nodeEdgeUnifier) addGraphPolicies(g routingGraph) error {
 // unifiedEdge is the individual channel data that is kept inside an edgeUnifier
 // object.
 type unifiedEdge struct {
-	policy   *channeldb.CachedEdgePolicy
+	policy   *models.CachedEdgePolicy
 	capacity btcutil.Amount
 }
 
@@ -233,7 +234,7 @@ func (u *edgeUnifier) getEdgeLocal(amt lnwire.MilliSatoshi,
 // forwarding context.
 func (u *edgeUnifier) getEdgeNetwork(amt lnwire.MilliSatoshi) *unifiedEdge {
 	var (
-		bestPolicy  *channeldb.CachedEdgePolicy
+		bestPolicy  *models.CachedEdgePolicy
 		maxFee      lnwire.MilliSatoshi
 		maxTimelock uint16
 		maxCapMsat  lnwire.MilliSatoshi
diff --git a/routing/validation_barrier.go b/routing/validation_barrier.go
index a7c6561ac..aeef3d4b8 100644
--- a/routing/validation_barrier.go
+++ b/routing/validation_barrier.go
@@ -5,6 +5,7 @@ import (
 	"sync"
 
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
 )
@@ -125,7 +126,7 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) {
 			v.nodeAnnDependencies[route.Vertex(msg.NodeID1)] = signals
 			v.nodeAnnDependencies[route.Vertex(msg.NodeID2)] = signals
 		}
-	case *channeldb.ChannelEdgeInfo:
+	case *models.ChannelEdgeInfo:
 
 		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
 		if _, ok := v.chanAnnFinSignal[shortID]; !ok {
@@ -143,7 +144,7 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) {
 
 	// These other types don't have any dependants, so no further
 	// initialization needs to be done beyond just occupying a job slot.
-	case *channeldb.ChannelEdgePolicy:
+	case *models.ChannelEdgePolicy:
 		return
 	case *lnwire.ChannelUpdate:
 		return
@@ -187,7 +188,7 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) error {
 	switch msg := job.(type) {
 	// Any ChannelUpdate or NodeAnnouncement jobs will need to wait on the
 	// completion of any active ChannelAnnouncement jobs related to them.
-	case *channeldb.ChannelEdgePolicy:
+	case *models.ChannelEdgePolicy:
 		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
 		signals, ok = v.chanEdgeDependencies[shortID]
 
@@ -217,7 +218,7 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) error {
 	// return directly.
 	case *lnwire.AnnounceSignatures:
 		// TODO(roasbeef): need to wait on chan ann?
-	case *channeldb.ChannelEdgeInfo:
+	case *models.ChannelEdgeInfo:
 	case *lnwire.ChannelAnnouncement:
 	}
 
@@ -263,7 +264,7 @@ func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) {
 	// If we've just finished executing a ChannelAnnouncement, then we'll
 	// close out the signal, and remove the signal from the map of active
 	// ones. This will allow/deny any dependent jobs to continue execution.
-	case *channeldb.ChannelEdgeInfo:
+	case *models.ChannelEdgeInfo:
 		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
 		finSignals, ok := v.chanAnnFinSignal[shortID]
 		if ok {
@@ -296,7 +297,7 @@ func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) {
 		delete(v.nodeAnnDependencies, route.Vertex(msg.NodeID))
 	case *lnwire.ChannelUpdate:
 		delete(v.chanEdgeDependencies, msg.ShortChannelID)
-	case *channeldb.ChannelEdgePolicy:
+	case *models.ChannelEdgePolicy:
 		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
 		delete(v.chanEdgeDependencies, shortID)
 
diff --git a/rpcserver.go b/rpcserver.go
index 85a156cf9..fa23f0485 100644
--- a/rpcserver.go
+++ b/rpcserver.go
@@ -38,6 +38,7 @@ import (
 	"github.com/lightningnetwork/lnd/chanbackup"
 	"github.com/lightningnetwork/lnd/chanfitness"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/channelnotifier"
 	"github.com/lightningnetwork/lnd/contractcourt"
 	"github.com/lightningnetwork/lnd/discovery"
@@ -5921,8 +5922,8 @@ func (r *rpcServer) DescribeGraph(ctx context.Context,
 	// Next, for each active channel we know of within the graph, create a
 	// similar response which details both the edge information as well as
 	// the routing policies of th nodes connecting the two edges.
-	err = graph.ForEachChannel(func(edgeInfo *channeldb.ChannelEdgeInfo,
-		c1, c2 *channeldb.ChannelEdgePolicy) error {
+	err = graph.ForEachChannel(func(edgeInfo *models.ChannelEdgeInfo,
+		c1, c2 *models.ChannelEdgePolicy) error {
 
 		// Do not include unannounced channels unless specifically
 		// requested. Unannounced channels include both private channels as
@@ -5932,7 +5933,7 @@ func (r *rpcServer) DescribeGraph(ctx context.Context,
 			return nil
 		}
 
-		edge := marshalDbEdge(edgeInfo, c1, c2)
+		edge := marshalDBEdge(edgeInfo, c1, c2)
 		resp.Edges = append(resp.Edges, edge)
 
 		return nil
@@ -5977,8 +5978,8 @@ func marshalExtraOpaqueData(data []byte) map[uint64][]byte {
 	return records
 }
 
-func marshalDbEdge(edgeInfo *channeldb.ChannelEdgeInfo,
-	c1, c2 *channeldb.ChannelEdgePolicy) *lnrpc.ChannelEdge {
+func marshalDBEdge(edgeInfo *models.ChannelEdgeInfo,
+	c1, c2 *models.ChannelEdgePolicy) *lnrpc.ChannelEdge {
 
 	// Make sure the policies match the node they belong to. c1 should point
 	// to the policy for NodeKey1, and c2 for NodeKey2.
@@ -6021,7 +6022,7 @@ func marshalDbEdge(edgeInfo *channeldb.ChannelEdgeInfo,
 }
 
 func marshalDBRoutingPolicy(
-	policy *channeldb.ChannelEdgePolicy) *lnrpc.RoutingPolicy {
+	policy *models.ChannelEdgePolicy) *lnrpc.RoutingPolicy {
 
 	disabled := policy.ChannelFlags&lnwire.ChanUpdateDisabled != 0
 
@@ -6113,7 +6114,7 @@ func (r *rpcServer) GetChanInfo(ctx context.Context,
 	// Convert the database's edge format into the network/RPC edge format
 	// which couples the edge itself along with the directional node
 	// routing policies of each node involved within the channel.
-	channelEdge := marshalDbEdge(edgeInfo, edge1, edge2)
+	channelEdge := marshalDBEdge(edgeInfo, edge1, edge2)
 
 	return channelEdge, nil
 }
@@ -6135,7 +6136,7 @@ func (r *rpcServer) GetNodeInfo(ctx context.Context,
 	// With the public key decoded, attempt to fetch the node corresponding
 	// to this public key. If the node cannot be found, then an error will
 	// be returned.
-	node, err := graph.FetchLightningNode(pubKey)
+	node, err := graph.FetchLightningNode(nil, pubKey)
 	switch {
 	case err == channeldb.ErrGraphNodeNotFound:
 		return nil, status.Error(codes.NotFound, err.Error())
@@ -6151,30 +6152,33 @@ func (r *rpcServer) GetNodeInfo(ctx context.Context,
 		channels      []*lnrpc.ChannelEdge
 	)
 
-	if err := node.ForEachChannel(nil, func(_ kvdb.RTx,
-		edge *channeldb.ChannelEdgeInfo,
-		c1, c2 *channeldb.ChannelEdgePolicy) error {
+	err = graph.ForEachNodeChannel(nil, node.PubKeyBytes,
+		func(_ kvdb.RTx, edge *models.ChannelEdgeInfo,
+			c1, c2 *models.ChannelEdgePolicy) error {
 
-		numChannels++
-		totalCapacity += edge.Capacity
+			numChannels++
+			totalCapacity += edge.Capacity
+
+			// Only populate the node's channels if the user
+			// requested them.
+			if in.IncludeChannels {
+				// Do not include unannounced channels - private
+				// channels or public channels whose
+				// authentication proof were not confirmed yet.
+				if edge.AuthProof == nil {
+					return nil
+				}
 
-		// Only populate the node's channels if the user requested them.
-		if in.IncludeChannels {
-			// Do not include unannounced channels - private
-			// channels or public channels whose authentication
-			// proof were not confirmed yet.
-			if edge.AuthProof == nil {
-				return nil
+				// Convert the database's edge format into the
+				// network/RPC edge format.
+				channelEdge := marshalDBEdge(edge, c1, c2)
+				channels = append(channels, channelEdge)
 			}
 
-			// Convert the database's edge format into the
-			// network/RPC edge format.
-			channelEdge := marshalDbEdge(edge, c1, c2)
-			channels = append(channels, channelEdge)
-		}
-
-		return nil
-	}); err != nil {
+			return nil
+		},
+	)
+	if err != nil {
 		return nil, err
 	}
 
@@ -6763,34 +6767,39 @@ func (r *rpcServer) FeeReport(ctx context.Context,
 	}
 
 	var feeReports []*lnrpc.ChannelFeeReport
-	err = selfNode.ForEachChannel(nil, func(_ kvdb.RTx, chanInfo *channeldb.ChannelEdgeInfo,
-		edgePolicy, _ *channeldb.ChannelEdgePolicy) error {
-
-		// Self node should always have policies for its channels.
-		if edgePolicy == nil {
-			return fmt.Errorf("no policy for outgoing channel %v ",
-				chanInfo.ChannelID)
-		}
-
-		// We'll compute the effective fee rate by converting from a
-		// fixed point fee rate to a floating point fee rate. The fee
-		// rate field in the database the amount of mSAT charged per
-		// 1mil mSAT sent, so will divide by this to get the proper fee
-		// rate.
-		feeRateFixedPoint := edgePolicy.FeeProportionalMillionths
-		feeRate := float64(feeRateFixedPoint) / feeBase
-
-		// TODO(roasbeef): also add stats for revenue for each channel
-		feeReports = append(feeReports, &lnrpc.ChannelFeeReport{
-			ChanId:       chanInfo.ChannelID,
-			ChannelPoint: chanInfo.ChannelPoint.String(),
-			BaseFeeMsat:  int64(edgePolicy.FeeBaseMSat),
-			FeePerMil:    int64(feeRateFixedPoint),
-			FeeRate:      feeRate,
-		})
+	err = channelGraph.ForEachNodeChannel(nil, selfNode.PubKeyBytes,
+		func(_ kvdb.RTx, chanInfo *models.ChannelEdgeInfo,
+			edgePolicy, _ *models.ChannelEdgePolicy) error {
+
+			// Self node should always have policies for its
+			// channels.
+			if edgePolicy == nil {
+				return fmt.Errorf("no policy for outgoing "+
+					"channel %v ", chanInfo.ChannelID)
+			}
 
-		return nil
-	})
+			// We'll compute the effective fee rate by converting
+			// from a fixed point fee rate to a floating point fee
+			// rate. The fee rate field in the database the amount
+			// of mSAT charged per 1mil mSAT sent, so will divide by
+			// this to get the proper fee rate.
+			feeRateFixedPoint :=
+				edgePolicy.FeeProportionalMillionths
+			feeRate := float64(feeRateFixedPoint) / feeBase
+
+			// TODO(roasbeef): also add stats for revenue for each
+			// channel
+			feeReports = append(feeReports, &lnrpc.ChannelFeeReport{
+				ChanId:       chanInfo.ChannelID,
+				ChannelPoint: chanInfo.ChannelPoint.String(),
+				BaseFeeMsat:  int64(edgePolicy.FeeBaseMSat),
+				FeePerMil:    int64(feeRateFixedPoint),
+				FeeRate:      feeRate,
+			})
+
+			return nil
+		},
+	)
 	if err != nil {
 		return nil, err
 	}
@@ -7099,7 +7108,7 @@ func (r *rpcServer) ForwardingHistory(ctx context.Context,
 			return "", err
 		}
 
-		peer, err := r.server.graphDB.FetchLightningNode(vertex)
+		peer, err := r.server.graphDB.FetchLightningNode(nil, vertex)
 		if err != nil {
 			return "", err
 		}
diff --git a/server.go b/server.go
index f3419a446..fcc489435 100644
--- a/server.go
+++ b/server.go
@@ -32,6 +32,7 @@ import (
 	"github.com/lightningnetwork/lnd/chanbackup"
 	"github.com/lightningnetwork/lnd/chanfitness"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/channelnotifier"
 	"github.com/lightningnetwork/lnd/clock"
 	"github.com/lightningnetwork/lnd/contractcourt"
@@ -1232,7 +1233,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
 	// Wrap the DeleteChannelEdges method so that the funding manager can
 	// use it without depending on several layers of indirection.
 	deleteAliasEdge := func(scid lnwire.ShortChannelID) (
-		*channeldb.ChannelEdgePolicy, error) {
+		*models.ChannelEdgePolicy, error) {
 
 		info, e1, e2, err := s.graphDB.FetchChannelEdgesByID(
 			scid.ToUint64(),
@@ -1251,7 +1252,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
 		var ourKey [33]byte
 		copy(ourKey[:], nodeKeyDesc.PubKey.SerializeCompressed())
 
-		var ourPolicy *channeldb.ChannelEdgePolicy
+		var ourPolicy *models.ChannelEdgePolicy
 		if info != nil && info.NodeKey1Bytes == ourKey {
 			ourPolicy = e1
 		} else {
@@ -3092,10 +3093,10 @@ func (s *server) establishPersistentConnections() error {
 	// TODO(roasbeef): instead iterate over link nodes and query graph for
 	// each of the nodes.
 	selfPub := s.identityECDH.PubKey().SerializeCompressed()
-	err = sourceNode.ForEachChannel(nil, func(
+	err = s.graphDB.ForEachNodeChannel(nil, sourceNode.PubKeyBytes, func(
 		tx kvdb.RTx,
-		chanInfo *channeldb.ChannelEdgeInfo,
-		policy, _ *channeldb.ChannelEdgePolicy) error {
+		chanInfo *models.ChannelEdgeInfo,
+		policy, _ *models.ChannelEdgePolicy) error {
 
 		// If the remote party has announced the channel to us, but we
 		// haven't yet, then we won't have a policy. However, we don't
@@ -3107,7 +3108,9 @@ func (s *server) establishPersistentConnections() error {
 
 		// We'll now fetch the peer opposite from us within this
 		// channel so we can queue up a direct connection to them.
-		channelPeer, err := chanInfo.FetchOtherNode(tx, selfPub)
+		channelPeer, err := s.graphDB.FetchOtherNode(
+			tx, chanInfo, selfPub,
+		)
 		if err != nil {
 			return fmt.Errorf("unable to fetch channel peer for "+
 				"ChannelPoint(%v): %v", chanInfo.ChannelPoint,
@@ -4594,7 +4597,7 @@ func (s *server) fetchNodeAdvertisedAddrs(pub *btcec.PublicKey) ([]net.Addr, err
 		return nil, err
 	}
 
-	node, err := s.graphDB.FetchLightningNode(vertex)
+	node, err := s.graphDB.FetchLightningNode(nil, vertex)
 	if err != nil {
 		return nil, err
 	}


Test patch:
diff --git a/channeldb/channel_cache_test.go b/channeldb/channel_cache_test.go
index d776c1318..7cb857293 100644
--- a/channeldb/channel_cache_test.go
+++ b/channeldb/channel_cache_test.go
@@ -3,6 +3,8 @@ package channeldb
 import (
 	"reflect"
 	"testing"
+
+	"github.com/lightningnetwork/lnd/channeldb/models"
 )
 
 // TestChannelCache checks the behavior of the channelCache with respect to
@@ -98,7 +100,7 @@ func assertHasChanEntries(t *testing.T, c *channelCache, start, end uint64) {
 // channelForInt generates a unique ChannelEdge given an integer.
 func channelForInt(i uint64) ChannelEdge {
 	return ChannelEdge{
-		Info: &ChannelEdgeInfo{
+		Info: &models.ChannelEdgeInfo{
 			ChannelID: i,
 		},
 	}
diff --git a/channeldb/graph_cache_test.go b/channeldb/graph_cache_test.go
index b408ec36d..f2b0c52f9 100644
--- a/channeldb/graph_cache_test.go
+++ b/channeldb/graph_cache_test.go
@@ -4,6 +4,7 @@ import (
 	"encoding/hex"
 	"testing"
 
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -28,9 +29,9 @@ type node struct {
 	pubKey   route.Vertex
 	features *lnwire.FeatureVector
 
-	edgeInfos   []*ChannelEdgeInfo
-	outPolicies []*ChannelEdgePolicy
-	inPolicies  []*ChannelEdgePolicy
+	edgeInfos   []*models.ChannelEdgeInfo
+	outPolicies []*models.ChannelEdgePolicy
+	inPolicies  []*models.ChannelEdgePolicy
 }
 
 func (n *node) PubKey() route.Vertex {
@@ -41,8 +42,8 @@ func (n *node) Features() *lnwire.FeatureVector {
 }
 
 func (n *node) ForEachChannel(tx kvdb.RTx,
-	cb func(kvdb.RTx, *ChannelEdgeInfo, *ChannelEdgePolicy,
-		*ChannelEdgePolicy) error) error {
+	cb func(kvdb.RTx, *models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+		*models.ChannelEdgePolicy) error) error {
 
 	for idx := range n.edgeInfos {
 		err := cb(
@@ -70,34 +71,28 @@ func TestGraphCacheAddNode(t *testing.T) {
 			channelFlagA, channelFlagB = 1, 0
 		}
 
-		outPolicy1 := &ChannelEdgePolicy{
+		outPolicy1 := &models.ChannelEdgePolicy{
 			ChannelID:    1000,
 			ChannelFlags: lnwire.ChanUpdateChanFlags(channelFlagA),
-			Node: &LightningNode{
-				PubKeyBytes: nodeB,
-				Features:    lnwire.EmptyFeatureVector(),
-			},
+			ToNode:       nodeB,
 		}
-		inPolicy1 := &ChannelEdgePolicy{
+		inPolicy1 := &models.ChannelEdgePolicy{
 			ChannelID:    1000,
 			ChannelFlags: lnwire.ChanUpdateChanFlags(channelFlagB),
-			Node: &LightningNode{
-				PubKeyBytes: nodeA,
-				Features:    lnwire.EmptyFeatureVector(),
-			},
+			ToNode:       nodeA,
 		}
 		node := &node{
 			pubKey:   nodeA,
 			features: lnwire.EmptyFeatureVector(),
-			edgeInfos: []*ChannelEdgeInfo{{
+			edgeInfos: []*models.ChannelEdgeInfo{{
 				ChannelID: 1000,
 				// Those are direction independent!
 				NodeKey1Bytes: pubKey1,
 				NodeKey2Bytes: pubKey2,
 				Capacity:      500,
 			}},
-			outPolicies: []*ChannelEdgePolicy{outPolicy1},
-			inPolicies:  []*ChannelEdgePolicy{inPolicy1},
+			outPolicies: []*models.ChannelEdgePolicy{outPolicy1},
+			inPolicies:  []*models.ChannelEdgePolicy{inPolicy1},
 		}
 		cache := NewGraphCache(10)
 		require.NoError(t, cache.AddNode(nil, node))
@@ -144,8 +139,8 @@ func TestGraphCacheAddNode(t *testing.T) {
 	runTest(pubKey2, pubKey1)
 }
 
-func assertCachedPolicyEqual(t *testing.T, original *ChannelEdgePolicy,
-	cached *CachedEdgePolicy) {
+func assertCachedPolicyEqual(t *testing.T, original *models.ChannelEdgePolicy,
+	cached *models.CachedEdgePolicy) {
 
 	require.Equal(t, original.ChannelID, cached.ChannelID)
 	require.Equal(t, original.MessageFlags, cached.MessageFlags)
@@ -159,9 +154,6 @@ func assertCachedPolicyEqual(t *testing.T, original *ChannelEdgePolicy,
 		cached.FeeProportionalMillionths,
 	)
 	require.Equal(
-		t,
-		route.Vertex(original.Node.PubKeyBytes),
-		cached.ToNodePubKey(),
+		t, route.Vertex(original.ToNode), cached.ToNodePubKey(),
 	)
-	require.Equal(t, original.Node.Features, cached.ToNodeFeatures)
 }
diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go
index 0afc9ea19..12e20a9e2 100644
--- a/channeldb/graph_test.go
+++ b/channeldb/graph_test.go
@@ -22,6 +22,7 @@ import (
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/davecgh/go-spew/spew"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -95,7 +96,6 @@ func createLightningNode(db kvdb.Backend, priv *btcec.PrivateKey) (*LightningNod
 		Alias:                "kek" + string(pub[:]),
 		Features:             testFeatures,
 		Addresses:            testAddrs,
-		db:                   db,
 	}
 	copy(n.PubKeyBytes[:], priv.PubKey().SerializeCompressed())
 
@@ -129,7 +129,6 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
 		Addresses:            testAddrs,
 		ExtraOpaqueData:      []byte("extra new data"),
 		PubKeyBytes:          testPub,
-		db:                   graph.db,
 	}
 
 	// First, insert the node into the graph DB. This should succeed
@@ -141,7 +140,7 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
 
 	// Next, fetch the node from the database to ensure everything was
 	// serialized properly.
-	dbNode, err := graph.FetchLightningNode(testPub)
+	dbNode, err := graph.FetchLightningNode(nil, testPub)
 	require.NoError(t, err, "unable to locate node")
 
 	if _, exists, err := graph.HasLightningNode(dbNode.PubKeyBytes); err != nil {
@@ -164,7 +163,7 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
 
 	// Finally, attempt to fetch the node again. This should fail as the
 	// node should have been deleted from the database.
-	_, err = graph.FetchLightningNode(testPub)
+	_, err = graph.FetchLightningNode(nil, testPub)
 	if err != ErrGraphNodeNotFound {
 		t.Fatalf("fetch after delete should fail!")
 	}
@@ -192,7 +191,7 @@ func TestPartialNode(t *testing.T) {
 
 	// Next, fetch the node from the database to ensure everything was
 	// serialized properly.
-	dbNode, err := graph.FetchLightningNode(testPub)
+	dbNode, err := graph.FetchLightningNode(nil, testPub)
 	require.NoError(t, err, "unable to locate node")
 
 	if _, exists, err := graph.HasLightningNode(dbNode.PubKeyBytes); err != nil {
@@ -207,7 +206,6 @@ func TestPartialNode(t *testing.T) {
 		HaveNodeAnnouncement: false,
 		LastUpdate:           time.Unix(0, 0),
 		PubKeyBytes:          testPub,
-		db:                   graph.db,
 	}
 
 	if err := compareNodes(node, dbNode); err != nil {
@@ -223,7 +221,7 @@ func TestPartialNode(t *testing.T) {
 
 	// Finally, attempt to fetch the node again. This should fail as the
 	// node should have been deleted from the database.
-	_, err = graph.FetchLightningNode(testPub)
+	_, err = graph.FetchLightningNode(nil, testPub)
 	if err != ErrGraphNodeNotFound {
 		t.Fatalf("fetch after delete should fail!")
 	}
@@ -327,10 +325,10 @@ func TestEdgeInsertionDeletion(t *testing.T) {
 	require.NoError(t, err, "unable to generate node key")
 	node2Pub, err := node2.PubKey()
 	require.NoError(t, err, "unable to generate node key")
-	edgeInfo := ChannelEdgeInfo{
+	edgeInfo := models.ChannelEdgeInfo{
 		ChannelID: chanID,
 		ChainHash: key,
-		AuthProof: &ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -387,7 +385,8 @@ func TestEdgeInsertionDeletion(t *testing.T) {
 }
 
 func createEdge(height, txIndex uint32, txPosition uint16, outPointIndex uint32,
-	node1, node2 *LightningNode) (ChannelEdgeInfo, lnwire.ShortChannelID) {
+	node1, node2 *LightningNode) (models.ChannelEdgeInfo,
+	lnwire.ShortChannelID) {
 
 	shortChanID := lnwire.ShortChannelID{
 		BlockHeight: height,
@@ -401,10 +400,10 @@ func createEdge(height, txIndex uint32, txPosition uint16, outPointIndex uint32,
 
 	node1Pub, _ := node1.PubKey()
 	node2Pub, _ := node2.PubKey()
-	edgeInfo := ChannelEdgeInfo{
+	edgeInfo := models.ChannelEdgeInfo{
 		ChannelID: shortChanID.ToUint64(),
 		ChainHash: key,
-		AuthProof: &ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -556,8 +555,8 @@ func TestDisconnectBlockAtHeight(t *testing.T) {
 	}
 }
 
-func assertEdgeInfoEqual(t *testing.T, e1 *ChannelEdgeInfo,
-	e2 *ChannelEdgeInfo) {
+func assertEdgeInfoEqual(t *testing.T, e1 *models.ChannelEdgeInfo,
+	e2 *models.ChannelEdgeInfo) {
 
 	if e1.ChannelID != e2.ChannelID {
 		t.Fatalf("chan id's don't match: %v vs %v", e1.ChannelID,
@@ -618,19 +617,20 @@ func assertEdgeInfoEqual(t *testing.T, e1 *ChannelEdgeInfo,
 	}
 }
 
-func createChannelEdge(db kvdb.Backend, node1, node2 *LightningNode) (*ChannelEdgeInfo,
-	*ChannelEdgePolicy, *ChannelEdgePolicy) {
+func createChannelEdge(db kvdb.Backend, node1, node2 *LightningNode) (
+	*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+	*models.ChannelEdgePolicy) {
 
 	var (
-		firstNode  *LightningNode
-		secondNode *LightningNode
+		firstNode  [33]byte
+		secondNode [33]byte
 	)
 	if bytes.Compare(node1.PubKeyBytes[:], node2.PubKeyBytes[:]) == -1 {
-		firstNode = node1
-		secondNode = node2
+		firstNode = node1.PubKeyBytes
+		secondNode = node2.PubKeyBytes
 	} else {
-		firstNode = node2
-		secondNode = node1
+		firstNode = node2.PubKeyBytes
+		secondNode = node1.PubKeyBytes
 	}
 
 	// In addition to the fake vertexes we create some fake channel
@@ -643,10 +643,10 @@ func createChannelEdge(db kvdb.Backend, node1, node2 *LightningNode) (*ChannelEd
 
 	// Add the new edge to the database, this should proceed without any
 	// errors.
-	edgeInfo := &ChannelEdgeInfo{
+	edgeInfo := &models.ChannelEdgeInfo{
 		ChannelID: chanID,
 		ChainHash: key,
-		AuthProof: &ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -656,12 +656,12 @@ func createChannelEdge(db kvdb.Backend, node1, node2 *LightningNode) (*ChannelEd
 		Capacity:        1000,
 		ExtraOpaqueData: []byte("new unknown feature"),
 	}
-	copy(edgeInfo.NodeKey1Bytes[:], firstNode.PubKeyBytes[:])
-	copy(edgeInfo.NodeKey2Bytes[:], secondNode.PubKeyBytes[:])
-	copy(edgeInfo.BitcoinKey1Bytes[:], firstNode.PubKeyBytes[:])
-	copy(edgeInfo.BitcoinKey2Bytes[:], secondNode.PubKeyBytes[:])
+	copy(edgeInfo.NodeKey1Bytes[:], firstNode[:])
+	copy(edgeInfo.NodeKey2Bytes[:], secondNode[:])
+	copy(edgeInfo.BitcoinKey1Bytes[:], firstNode[:])
+	copy(edgeInfo.BitcoinKey2Bytes[:], secondNode[:])
 
-	edge1 := &ChannelEdgePolicy{
+	edge1 := &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 chanID,
 		LastUpdate:                time.Unix(433453, 0),
@@ -672,11 +672,10 @@ func createChannelEdge(db kvdb.Backend, node1, node2 *LightningNode) (*ChannelEd
 		MaxHTLC:                   13928598,
 		FeeBaseMSat:               4352345,
 		FeeProportionalMillionths: 3452352,
-		Node:                      secondNode,
+		ToNode:                    secondNode,
 		ExtraOpaqueData:           []byte("new unknown feature2"),
-		db:                        db,
 	}
-	edge2 := &ChannelEdgePolicy{
+	edge2 := &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 chanID,
 		LastUpdate:                time.Unix(124234, 0),
@@ -687,9 +686,8 @@ func createChannelEdge(db kvdb.Backend, node1, node2 *LightningNode) (*ChannelEd
 		MaxHTLC:                   13928598,
 		FeeBaseMSat:               4352345,
 		FeeProportionalMillionths: 90392423,
-		Node:                      firstNode,
+		ToNode:                    firstNode,
 		ExtraOpaqueData:           []byte("new unknown feature1"),
-		db:                        db,
 	}
 
 	return edgeInfo, edge1, edge2
@@ -822,7 +820,7 @@ func assertNodeNotInCache(t *testing.T, g *ChannelGraph, n route.Vertex) {
 }
 
 func assertEdgeWithNoPoliciesInCache(t *testing.T, g *ChannelGraph,
-	e *ChannelEdgeInfo) {
+	e *models.ChannelEdgeInfo) {
 
 	// Let's check the internal view first.
 	require.NotEmpty(t, g.graphCache.nodeChannels[e.NodeKey1Bytes])
@@ -900,7 +898,7 @@ func assertNoEdge(t *testing.T, g *ChannelGraph, chanID uint64) {
 }
 
 func assertEdgeWithPolicyInCache(t *testing.T, g *ChannelGraph,
-	e *ChannelEdgeInfo, p *ChannelEdgePolicy, policy1 bool) {
+	e *models.ChannelEdgeInfo, p *models.ChannelEdgePolicy, policy1 bool) {
 
 	// Check the internal state first.
 	c1, ok := g.graphCache.nodeChannels[e.NodeKey1Bytes][e.ChannelID]
@@ -976,16 +974,16 @@ func assertEdgeWithPolicyInCache(t *testing.T, g *ChannelGraph,
 	}
 }
 
-func randEdgePolicy(chanID uint64, db kvdb.Backend) *ChannelEdgePolicy {
+func randEdgePolicy(chanID uint64, db kvdb.Backend) *models.ChannelEdgePolicy {
 	update := prand.Int63()
 
 	return newEdgePolicy(chanID, db, update)
 }
 
 func newEdgePolicy(chanID uint64, db kvdb.Backend,
-	updateTime int64) *ChannelEdgePolicy {
+	updateTime int64) *models.ChannelEdgePolicy {
 
-	return &ChannelEdgePolicy{
+	return &models.ChannelEdgePolicy{
 		ChannelID:                 chanID,
 		LastUpdate:                time.Unix(updateTime, 0),
 		MessageFlags:              1,
@@ -995,7 +993,6 @@ func newEdgePolicy(chanID uint64, db kvdb.Backend,
 		MaxHTLC:                   lnwire.MilliSatoshi(prand.Int63()),
 		FeeBaseMSat:               lnwire.MilliSatoshi(prand.Int63()),
 		FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int63()),
-		db:                        db,
 	}
 }
 
@@ -1043,8 +1040,9 @@ func TestGraphTraversal(t *testing.T) {
 	// Iterate through all the known channels within the graph DB, once
 	// again if the map is empty that indicates that all edges have
 	// properly been reached.
-	err = graph.ForEachChannel(func(ei *ChannelEdgeInfo, _ *ChannelEdgePolicy,
-		_ *ChannelEdgePolicy) error {
+	err = graph.ForEachChannel(func(ei *models.ChannelEdgeInfo,
+		_ *models.ChannelEdgePolicy,
+		_ *models.ChannelEdgePolicy) error {
 
 		delete(chanIndex, ei.ChannelID)
 		return nil
@@ -1056,30 +1054,38 @@ func TestGraphTraversal(t *testing.T) {
 	// outgoing channels for a particular node.
 	numNodeChans := 0
 	firstNode, secondNode := nodeList[0], nodeList[1]
-	err = firstNode.ForEachChannel(nil, func(_ kvdb.RTx, _ *ChannelEdgeInfo,
-		outEdge, inEdge *ChannelEdgePolicy) error {
+	err = graph.ForEachNodeChannel(nil, firstNode.PubKeyBytes,
+		func(_ kvdb.RTx, _ *models.ChannelEdgeInfo, outEdge,
+			inEdge *models.ChannelEdgePolicy) error {
+
+			// All channels between first and second node should
+			// have fully (both sides) specified policies.
+			if inEdge == nil || outEdge == nil {
+				return fmt.Errorf("channel policy not present")
+			}
 
-		// All channels between first and second node should have fully
-		// (both sides) specified policies.
-		if inEdge == nil || outEdge == nil {
-			return fmt.Errorf("channel policy not present")
-		}
+			// Each should indicate that it's outgoing (pointed
+			// towards the second node).
+			if !bytes.Equal(
+				outEdge.ToNode[:], secondNode.PubKeyBytes[:],
+			) {
 
-		// Each should indicate that it's outgoing (pointed
-		// towards the second node).
-		if !bytes.Equal(outEdge.Node.PubKeyBytes[:], secondNode.PubKeyBytes[:]) {
-			return fmt.Errorf("wrong outgoing edge")
-		}
+				return fmt.Errorf("wrong outgoing edge")
+			}
 
-		// The incoming edge should also indicate that it's pointing to
-		// the origin node.
-		if !bytes.Equal(inEdge.Node.PubKeyBytes[:], firstNode.PubKeyBytes[:]) {
-			return fmt.Errorf("wrong outgoing edge")
-		}
+			// The incoming edge should also indicate that it's
+			// pointing to the origin node.
+			if !bytes.Equal(
+				inEdge.ToNode[:], firstNode.PubKeyBytes[:],
+			) {
 
-		numNodeChans++
-		return nil
-	})
+				return fmt.Errorf("wrong outgoing edge")
+			}
+
+			numNodeChans++
+
+			return nil
+		})
 	require.NoError(t, err)
 	require.Equal(t, numChannels, numNodeChans)
 }
@@ -1129,9 +1135,10 @@ func TestGraphTraversalCacheable(t *testing.T) {
 	err = graph.db.View(func(tx kvdb.RTx) error {
 		for _, node := range nodes {
 			err := node.ForEachChannel(
-				tx, func(tx kvdb.RTx, info *ChannelEdgeInfo,
-					policy *ChannelEdgePolicy,
-					policy2 *ChannelEdgePolicy) error {
+				tx, func(tx kvdb.RTx,
+					info *models.ChannelEdgeInfo,
+					policy *models.ChannelEdgePolicy,
+					policy2 *models.ChannelEdgePolicy) error { //nolint:lll
 
 					delete(chanIndex, info.ChannelID)
 					return nil
@@ -1249,10 +1256,10 @@ func fillTestGraph(t require.TestingT, graph *ChannelGraph, numNodes,
 				Index: 0,
 			}
 
-			edgeInfo := ChannelEdgeInfo{
+			edgeInfo := models.ChannelEdgeInfo{
 				ChannelID: chanID,
 				ChainHash: key,
-				AuthProof: &ChannelAuthProof{
+				AuthProof: &models.ChannelAuthProof{
 					NodeSig1Bytes:    testSig.Serialize(),
 					NodeSig2Bytes:    testSig.Serialize(),
 					BitcoinSig1Bytes: testSig.Serialize(),
@@ -1272,7 +1279,7 @@ func fillTestGraph(t require.TestingT, graph *ChannelGraph, numNodes,
 			// from node1 -> node2.
 			edge := randEdgePolicy(chanID, graph.db)
 			edge.ChannelFlags = 0
-			edge.Node = node2
+			edge.ToNode = node2.PubKeyBytes
 			edge.SigBytes = testSig.Serialize()
 			require.NoError(t, graph.UpdateEdgePolicy(edge))
 
@@ -1280,7 +1287,7 @@ func fillTestGraph(t require.TestingT, graph *ChannelGraph, numNodes,
 			// node2 -> node1 this time.
 			edge = randEdgePolicy(chanID, graph.db)
 			edge.ChannelFlags = 1
-			edge.Node = node1
+			edge.ToNode = node1.PubKeyBytes
 			edge.SigBytes = testSig.Serialize()
 			require.NoError(t, graph.UpdateEdgePolicy(edge))
 
@@ -1313,8 +1320,9 @@ func assertPruneTip(t *testing.T, graph *ChannelGraph, blockHash *chainhash.Hash
 
 func assertNumChans(t *testing.T, graph *ChannelGraph, n int) {
 	numChans := 0
-	if err := graph.ForEachChannel(func(*ChannelEdgeInfo, *ChannelEdgePolicy,
-		*ChannelEdgePolicy) error {
+	if err := graph.ForEachChannel(func(*models.ChannelEdgeInfo,
+		*models.ChannelEdgePolicy,
+		*models.ChannelEdgePolicy) error {
 
 		numChans++
 		return nil
@@ -1430,10 +1438,10 @@ func TestGraphPruning(t *testing.T) {
 
 		channelPoints = append(channelPoints, &op)
 
-		edgeInfo := ChannelEdgeInfo{
+		edgeInfo := models.ChannelEdgeInfo{
 			ChannelID: chanID,
 			ChainHash: key,
-			AuthProof: &ChannelAuthProof{
+			AuthProof: &models.ChannelAuthProof{
 				NodeSig1Bytes:    testSig.Serialize(),
 				NodeSig2Bytes:    testSig.Serialize(),
 				BitcoinSig1Bytes: testSig.Serialize(),
@@ -1465,7 +1473,7 @@ func TestGraphPruning(t *testing.T) {
 		// node_i -> node_i+1
 		edge := randEdgePolicy(chanID, graph.db)
 		edge.ChannelFlags = 0
-		edge.Node = graphNodes[i]
+		edge.ToNode = graphNodes[i].PubKeyBytes
 		edge.SigBytes = testSig.Serialize()
 		if err := graph.UpdateEdgePolicy(edge); err != nil {
 			t.Fatalf("unable to update edge: %v", err)
@@ -1475,7 +1483,7 @@ func TestGraphPruning(t *testing.T) {
 		// node_i this time.
 		edge = randEdgePolicy(chanID, graph.db)
 		edge.ChannelFlags = 1
-		edge.Node = graphNodes[i]
+		edge.ToNode = graphNodes[i].PubKeyBytes
 		edge.SigBytes = testSig.Serialize()
 		if err := graph.UpdateEdgePolicy(edge); err != nil {
 			t.Fatalf("unable to update edge: %v", err)
@@ -1690,7 +1698,7 @@ func TestChanUpdatesInHorizon(t *testing.T) {
 			chanID.ToUint64(), graph.db, edge1UpdateTime.Unix(),
 		)
 		edge1.ChannelFlags = 0
-		edge1.Node = node2
+		edge1.ToNode = node2.PubKeyBytes
 		edge1.SigBytes = testSig.Serialize()
 		if err := graph.UpdateEdgePolicy(edge1); err != nil {
 			t.Fatalf("unable to update edge: %v", err)
@@ -1700,7 +1708,7 @@ func TestChanUpdatesInHorizon(t *testing.T) {
 			chanID.ToUint64(), graph.db, edge2UpdateTime.Unix(),
 		)
 		edge2.ChannelFlags = 1
-		edge2.Node = node1
+		edge2.ToNode = node1.PubKeyBytes
 		edge2.SigBytes = testSig.Serialize()
 		if err := graph.UpdateEdgePolicy(edge2); err != nil {
 			t.Fatalf("unable to update edge: %v", err)
@@ -2184,7 +2192,7 @@ func TestFetchChanInfos(t *testing.T) {
 			chanID.ToUint64(), graph.db, updateTime.Unix(),
 		)
 		edge1.ChannelFlags = 0
-		edge1.Node = node2
+		edge1.ToNode = node2.PubKeyBytes
 		edge1.SigBytes = testSig.Serialize()
 		if err := graph.UpdateEdgePolicy(edge1); err != nil {
 			t.Fatalf("unable to update edge: %v", err)
@@ -2194,7 +2202,7 @@ func TestFetchChanInfos(t *testing.T) {
 			chanID.ToUint64(), graph.db, updateTime.Unix(),
 		)
 		edge2.ChannelFlags = 1
-		edge2.Node = node1
+		edge2.ToNode = node1.PubKeyBytes
 		edge2.SigBytes = testSig.Serialize()
 		if err := graph.UpdateEdgePolicy(edge2); err != nil {
 			t.Fatalf("unable to update edge: %v", err)
@@ -2280,29 +2288,30 @@ func TestIncompleteChannelPolicies(t *testing.T) {
 	// Ensure that channel is reported with unknown policies.
 	checkPolicies := func(node *LightningNode, expectedIn, expectedOut bool) {
 		calls := 0
-		err := node.ForEachChannel(nil, func(_ kvdb.RTx, _ *ChannelEdgeInfo,
-			outEdge, inEdge *ChannelEdgePolicy) error {
+		err := graph.ForEachNodeChannel(nil, node.PubKeyBytes,
+			func(_ kvdb.RTx, _ *models.ChannelEdgeInfo, outEdge,
+				inEdge *models.ChannelEdgePolicy) error {
 
-			if !expectedOut && outEdge != nil {
-				t.Fatalf("Expected no outgoing policy")
-			}
+				if !expectedOut && outEdge != nil {
+					t.Fatalf("Expected no outgoing policy")
+				}
 
-			if expectedOut && outEdge == nil {
-				t.Fatalf("Expected an outgoing policy")
-			}
+				if expectedOut && outEdge == nil {
+					t.Fatalf("Expected an outgoing policy")
+				}
 
-			if !expectedIn && inEdge != nil {
-				t.Fatalf("Expected no incoming policy")
-			}
+				if !expectedIn && inEdge != nil {
+					t.Fatalf("Expected no incoming policy")
+				}
 
-			if expectedIn && inEdge == nil {
-				t.Fatalf("Expected an incoming policy")
-			}
+				if expectedIn && inEdge == nil {
+					t.Fatalf("Expected an incoming policy")
+				}
 
-			calls++
+				calls++
 
-			return nil
-		})
+				return nil
+			})
 		if err != nil {
 			t.Fatalf("unable to scan channels: %v", err)
 		}
@@ -2322,7 +2331,7 @@ func TestIncompleteChannelPolicies(t *testing.T) {
 		chanID.ToUint64(), graph.db, updateTime.Unix(),
 	)
 	edgePolicy.ChannelFlags = 0
-	edgePolicy.Node = node2
+	edgePolicy.ToNode = node2.PubKeyBytes
 	edgePolicy.SigBytes = testSig.Serialize()
 	if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
@@ -2337,7 +2346,7 @@ func TestIncompleteChannelPolicies(t *testing.T) {
 		chanID.ToUint64(), graph.db, updateTime.Unix(),
 	)
 	edgePolicy.ChannelFlags = 1
-	edgePolicy.Node = node1
+	edgePolicy.ToNode = node1.PubKeyBytes
 	edgePolicy.SigBytes = testSig.Serialize()
 	if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
@@ -2384,7 +2393,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) {
 
 	edge1 := randEdgePolicy(chanID.ToUint64(), graph.db)
 	edge1.ChannelFlags = 0
-	edge1.Node = node1
+	edge1.ToNode = node1.PubKeyBytes
 	edge1.SigBytes = testSig.Serialize()
 	if err := graph.UpdateEdgePolicy(edge1); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
@@ -2392,7 +2401,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) {
 
 	edge2 := randEdgePolicy(chanID.ToUint64(), graph.db)
 	edge2.ChannelFlags = 1
-	edge2.Node = node2
+	edge2.ToNode = node2.PubKeyBytes
 	edge2.SigBytes = testSig.Serialize()
 	if err := graph.UpdateEdgePolicy(edge2); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
@@ -2537,7 +2546,7 @@ func TestPruneGraphNodes(t *testing.T) {
 	// points from the first to the second node.
 	edge1 := randEdgePolicy(chanID.ToUint64(), graph.db)
 	edge1.ChannelFlags = 0
-	edge1.Node = node1
+	edge1.ToNode = node1.PubKeyBytes
 	edge1.SigBytes = testSig.Serialize()
 	if err := graph.UpdateEdgePolicy(edge1); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
@@ -2556,7 +2565,7 @@ func TestPruneGraphNodes(t *testing.T) {
 
 	// Finally, we'll ensure that node3, the only fully unconnected node as
 	// properly deleted from the graph and not another node in its place.
-	_, err = graph.FetchLightningNode(node3.PubKeyBytes)
+	_, err = graph.FetchLightningNode(nil, node3.PubKeyBytes)
 	if err == nil {
 		t.Fatalf("node 3 should have been deleted!")
 	}
@@ -2590,13 +2599,13 @@ func TestAddChannelEdgeShellNodes(t *testing.T) {
 
 	// Ensure that node1 was inserted as a full node, while node2 only has
 	// a shell node present.
-	node1, err = graph.FetchLightningNode(node1.PubKeyBytes)
+	node1, err = graph.FetchLightningNode(nil, node1.PubKeyBytes)
 	require.NoError(t, err, "unable to fetch node1")
 	if !node1.HaveNodeAnnouncement {
 		t.Fatalf("have shell announcement for node1, shouldn't")
 	}
 
-	node2, err = graph.FetchLightningNode(node2.PubKeyBytes)
+	node2, err = graph.FetchLightningNode(nil, node2.PubKeyBytes)
 	require.NoError(t, err, "unable to fetch node2")
 	if node2.HaveNodeAnnouncement {
 		t.Fatalf("should have shell announcement for node2, but is full")
@@ -2698,18 +2707,15 @@ func TestNodeIsPublic(t *testing.T) {
 	// After creating all of our nodes and edges, we'll add them to each
 	// participant's graph.
 	nodes := []*LightningNode{aliceNode, bobNode, carolNode}
-	edges := []*ChannelEdgeInfo{&aliceBobEdge, &bobCarolEdge}
-	dbs := []kvdb.Backend{aliceGraph.db, bobGraph.db, carolGraph.db}
+	edges := []*models.ChannelEdgeInfo{&aliceBobEdge, &bobCarolEdge}
 	graphs := []*ChannelGraph{aliceGraph, bobGraph, carolGraph}
-	for i, graph := range graphs {
+	for _, graph := range graphs {
 		for _, node := range nodes {
-			node.db = dbs[i]
 			if err := graph.AddLightningNode(node); err != nil {
 				t.Fatalf("unable to add node: %v", err)
 			}
 		}
 		for _, edge := range edges {
-			edge.db = dbs[i]
 			if err := graph.AddChannelEdge(edge); err != nil {
 				t.Fatalf("unable to add edge: %v", err)
 			}
@@ -2769,7 +2775,7 @@ func TestNodeIsPublic(t *testing.T) {
 	// that allows it to be advertised. Within Alice's graph, we'll
 	// completely remove the edge as it is not possible for her to know of
 	// it without it being advertised.
-	for i, graph := range graphs {
+	for _, graph := range graphs {
 		err := graph.DeleteChannelEdges(
 			false, true, bobCarolEdge.ChannelID,
 		)
@@ -2782,7 +2788,6 @@ func TestNodeIsPublic(t *testing.T) {
 		}
 
 		bobCarolEdge.AuthProof = nil
-		bobCarolEdge.db = dbs[i]
 		if err := graph.AddChannelEdge(&bobCarolEdge); err != nil {
 			t.Fatalf("unable to add edge: %v", err)
 		}
@@ -2908,8 +2913,8 @@ func TestEdgePolicyMissingMaxHtcl(t *testing.T) {
 	}
 
 	chanID := edgeInfo.ChannelID
-	from := edge2.Node.PubKeyBytes[:]
-	to := edge1.Node.PubKeyBytes[:]
+	from := edge2.ToNode[:]
+	to := edge1.ToNode[:]
 
 	// We'll remove the no max_htlc field from the first edge policy, and
 	// all other opaque data, and serialize it.
@@ -2945,7 +2950,7 @@ func TestEdgePolicyMissingMaxHtcl(t *testing.T) {
 			return ErrGraphNotFound
 		}
 
-		_, err = deserializeChanEdgePolicy(r, nodes)
+		_, err = deserializeChanEdgePolicy(r)
 		if err != ErrEdgePolicyOptionalFieldNotFound {
 			t.Fatalf("expected "+
 				"ErrEdgePolicyOptionalFieldNotFound, got %v",
@@ -3140,10 +3145,6 @@ func compareNodes(a, b *LightningNode) error {
 		return fmt.Errorf("Alias doesn't match: expected %#v, \n "+
 			"got %#v", a.Alias, b.Alias)
 	}
-	if !reflect.DeepEqual(a.db, b.db) {
-		return fmt.Errorf("db doesn't match: expected %#v, \n "+
-			"got %#v", a.db, b.db)
-	}
 	if !reflect.DeepEqual(a.HaveNodeAnnouncement, b.HaveNodeAnnouncement) {
 		return fmt.Errorf("HaveNodeAnnouncement doesn't match: expected %#v, \n "+
 			"got %#v", a.HaveNodeAnnouncement, b.HaveNodeAnnouncement)
@@ -3158,7 +3159,7 @@ func compareNodes(a, b *LightningNode) error {
 
 // compareEdgePolicies is used to compare two ChannelEdgePolices using
 // compareNodes, so as to exclude comparisons of the Nodes' Features struct.
-func compareEdgePolicies(a, b *ChannelEdgePolicy) error {
+func compareEdgePolicies(a, b *models.ChannelEdgePolicy) error {
 	if a.ChannelID != b.ChannelID {
 		return fmt.Errorf("ChannelID doesn't match: expected %v, "+
 			"got %v", a.ChannelID, b.ChannelID)
@@ -3200,13 +3201,11 @@ func compareEdgePolicies(a, b *ChannelEdgePolicy) error {
 		return fmt.Errorf("extra data doesn't match: %v vs %v",
 			a.ExtraOpaqueData, b.ExtraOpaqueData)
 	}
-	if err := compareNodes(a.Node, b.Node); err != nil {
-		return err
-	}
-	if !reflect.DeepEqual(a.db, b.db) {
-		return fmt.Errorf("db doesn't match: expected %#v, \n "+
-			"got %#v", a.db, b.db)
+	if !bytes.Equal(a.ToNode[:], b.ToNode[:]) {
+		return fmt.Errorf("ToNode doesn't match: expected %x, got %x",
+			a.ToNode, b.ToNode)
 	}
+
 	return nil
 }
 
@@ -3252,7 +3251,7 @@ func TestLightningNodeSigVerification(t *testing.T) {
 // TestComputeFee tests fee calculation based on both in- and outgoing amt.
 func TestComputeFee(t *testing.T) {
 	var (
-		policy = ChannelEdgePolicy{
+		policy = models.ChannelEdgePolicy{
 			FeeBaseMSat:               10000,
 			FeeProportionalMillionths: 30000,
 		}
@@ -3326,7 +3325,7 @@ func TestBatchedAddChannelEdge(t *testing.T) {
 	// Create a third edge, this with a block height of 155.
 	edgeInfo3, _ := createEdge(height-1, 0, 0, 2, node1, node2)
 
-	edges := []ChannelEdgeInfo{edgeInfo, edgeInfo2, edgeInfo3}
+	edges := []models.ChannelEdgeInfo{edgeInfo, edgeInfo2, edgeInfo3}
 	errChan := make(chan error, len(edges))
 	errTimeout := errors.New("timeout adding batched channel")
 
@@ -3334,7 +3333,7 @@ func TestBatchedAddChannelEdge(t *testing.T) {
 	var wg sync.WaitGroup
 	for _, edge := range edges {
 		wg.Add(1)
-		go func(edge ChannelEdgeInfo) {
+		go func(edge models.ChannelEdgeInfo) {
 			defer wg.Done()
 
 			select {
@@ -3385,7 +3384,7 @@ func TestBatchedUpdateEdgePolicy(t *testing.T) {
 
 	errTimeout := errors.New("timeout adding batched channel")
 
-	updates := []*ChannelEdgePolicy{edge1, edge2}
+	updates := []*models.ChannelEdgePolicy{edge1, edge2}
 
 	errChan := make(chan error, len(updates))
 
@@ -3393,7 +3392,7 @@ func TestBatchedUpdateEdgePolicy(t *testing.T) {
 	var wg sync.WaitGroup
 	for _, update := range updates {
 		wg.Add(1)
-		go func(update *ChannelEdgePolicy) {
+		go func(update *models.ChannelEdgePolicy) {
 			defer wg.Done()
 
 			select {
@@ -3441,24 +3440,24 @@ func BenchmarkForEachChannel(b *testing.B) {
 
 		err = graph.db.View(func(tx kvdb.RTx) error {
 			for _, n := range nodes {
-				err := n.ForEachChannel(
-					tx, func(tx kvdb.RTx,
-						info *ChannelEdgeInfo,
-						policy *ChannelEdgePolicy,
-						policy2 *ChannelEdgePolicy) error {
-
-						// We need to do something with
-						// the data here, otherwise the
-						// compiler is going to optimize
-						// this away, and we get bogus
-						// results.
-						totalCapacity += info.Capacity
-						maxHTLCs += policy.MaxHTLC
-						maxHTLCs += policy2.MaxHTLC
-
-						return nil
-					},
-				)
+				cb := func(tx kvdb.RTx,
+					info *models.ChannelEdgeInfo,
+					policy *models.ChannelEdgePolicy,
+					policy2 *models.ChannelEdgePolicy) error { //nolint:lll
+
+					// We need to do something with
+					// the data here, otherwise the
+					// compiler is going to optimize
+					// this away, and we get bogus
+					// results.
+					totalCapacity += info.Capacity
+					maxHTLCs += policy.MaxHTLC
+					maxHTLCs += policy2.MaxHTLC
+
+					return nil
+				}
+
+				err := n.ForEachChannel(tx, cb)
 				if err != nil {
 					return err
 				}
@@ -3470,8 +3469,8 @@ func BenchmarkForEachChannel(b *testing.B) {
 	}
 }
 
-// TestGraphCacheForEachNodeChannel tests that the ForEachNodeChannel method
-// works as expected, and is able to handle nil self edges.
+// TestGraphCacheForEachNodeChannel tests that the ForEachNodeDirectedChannel
+// method works as expected, and is able to handle nil self edges.
 func TestGraphCacheForEachNodeChannel(t *testing.T) {
 	graph, err := MakeTestGraph(t)
 	require.NoError(t, err)
@@ -3498,11 +3497,13 @@ func TestGraphCacheForEachNodeChannel(t *testing.T) {
 	// We should be able to accumulate the single channel added, even
 	// though we have a nil edge policy here.
 	var numChans int
-	err = graph.ForEachNodeChannel(nil, node1.PubKeyBytes,
-		func(channel *DirectedChannel) error {
+	err = graph.ForEachNodeDirectedChannel(nil, node1.PubKeyBytes,
+		func(_ *DirectedChannel) error {
 			numChans++
+
 			return nil
-		})
+		},
+	)
 	require.NoError(t, err)
 
 	require.Equal(t, numChans, 1)
diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go
index d364b80ec..a47d98bb9 100644
--- a/discovery/gossiper_test.go
+++ b/discovery/gossiper_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/lightningnetwork/lnd/batch"
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnpeer"
@@ -91,8 +92,8 @@ type mockGraphSource struct {
 
 	mu            sync.Mutex
 	nodes         []channeldb.LightningNode
-	infos         map[uint64]channeldb.ChannelEdgeInfo
-	edges         map[uint64][]channeldb.ChannelEdgePolicy
+	infos         map[uint64]models.ChannelEdgeInfo
+	edges         map[uint64][]models.ChannelEdgePolicy
 	zombies       map[uint64][][33]byte
 	chansToReject map[uint64]struct{}
 }
@@ -100,8 +101,8 @@ type mockGraphSource struct {
 func newMockRouter(height uint32) *mockGraphSource {
 	return &mockGraphSource{
 		bestHeight:    height,
-		infos:         make(map[uint64]channeldb.ChannelEdgeInfo),
-		edges:         make(map[uint64][]channeldb.ChannelEdgePolicy),
+		infos:         make(map[uint64]models.ChannelEdgeInfo),
+		edges:         make(map[uint64][]models.ChannelEdgePolicy),
 		zombies:       make(map[uint64][][33]byte),
 		chansToReject: make(map[uint64]struct{}),
 	}
@@ -119,7 +120,7 @@ func (r *mockGraphSource) AddNode(node *channeldb.LightningNode,
 	return nil
 }
 
-func (r *mockGraphSource) AddEdge(info *channeldb.ChannelEdgeInfo,
+func (r *mockGraphSource) AddEdge(info *models.ChannelEdgeInfo,
 	_ ...batch.SchedulerOption) error {
 
 	r.mu.Lock()
@@ -144,14 +145,14 @@ func (r *mockGraphSource) queueValidationFail(chanID uint64) {
 	r.chansToReject[chanID] = struct{}{}
 }
 
-func (r *mockGraphSource) UpdateEdge(edge *channeldb.ChannelEdgePolicy,
+func (r *mockGraphSource) UpdateEdge(edge *models.ChannelEdgePolicy,
 	_ ...batch.SchedulerOption) error {
 
 	r.mu.Lock()
 	defer r.mu.Unlock()
 
 	if len(r.edges[edge.ChannelID]) == 0 {
-		r.edges[edge.ChannelID] = make([]channeldb.ChannelEdgePolicy, 2)
+		r.edges[edge.ChannelID] = make([]models.ChannelEdgePolicy, 2)
 	}
 
 	if edge.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
@@ -168,7 +169,7 @@ func (r *mockGraphSource) CurrentBlockHeight() (uint32, error) {
 }
 
 func (r *mockGraphSource) AddProof(chanID lnwire.ShortChannelID,
-	proof *channeldb.ChannelAuthProof) error {
+	proof *models.ChannelAuthProof) error {
 
 	r.mu.Lock()
 	defer r.mu.Unlock()
@@ -190,8 +191,8 @@ func (r *mockGraphSource) ForEachNode(func(node *channeldb.LightningNode) error)
 }
 
 func (r *mockGraphSource) ForAllOutgoingChannels(cb func(tx kvdb.RTx,
-	i *channeldb.ChannelEdgeInfo,
-	c *channeldb.ChannelEdgePolicy) error) error {
+	i *models.ChannelEdgeInfo,
+	c *models.ChannelEdgePolicy) error) error {
 
 	r.mu.Lock()
 	defer r.mu.Unlock()
@@ -221,15 +222,10 @@ func (r *mockGraphSource) ForAllOutgoingChannels(cb func(tx kvdb.RTx,
 	return nil
 }
 
-func (r *mockGraphSource) ForEachChannel(func(chanInfo *channeldb.ChannelEdgeInfo,
-	e1, e2 *channeldb.ChannelEdgePolicy) error) error {
-	return nil
-}
-
 func (r *mockGraphSource) GetChannelByID(chanID lnwire.ShortChannelID) (
-	*channeldb.ChannelEdgeInfo,
-	*channeldb.ChannelEdgePolicy,
-	*channeldb.ChannelEdgePolicy, error) {
+	*models.ChannelEdgeInfo,
+	*models.ChannelEdgePolicy,
+	*models.ChannelEdgePolicy, error) {
 
 	r.mu.Lock()
 	defer r.mu.Unlock()
@@ -242,7 +238,7 @@ func (r *mockGraphSource) GetChannelByID(chanID lnwire.ShortChannelID) (
 			return nil, nil, nil, channeldb.ErrEdgeNotFound
 		}
 
-		return &channeldb.ChannelEdgeInfo{
+		return &models.ChannelEdgeInfo{
 			NodeKey1Bytes: pubKeys[0],
 			NodeKey2Bytes: pubKeys[1],
 		}, nil, nil, channeldb.ErrZombieEdge
@@ -253,13 +249,13 @@ func (r *mockGraphSource) GetChannelByID(chanID lnwire.ShortChannelID) (
 		return &chanInfo, nil, nil, nil
 	}
 
-	var edge1 *channeldb.ChannelEdgePolicy
-	if !reflect.DeepEqual(edges[0], channeldb.ChannelEdgePolicy{}) {
+	var edge1 *models.ChannelEdgePolicy
+	if !reflect.DeepEqual(edges[0], models.ChannelEdgePolicy{}) {
 		edge1 = &edges[0]
 	}
 
-	var edge2 *channeldb.ChannelEdgePolicy
-	if !reflect.DeepEqual(edges[1], channeldb.ChannelEdgePolicy{}) {
+	var edge2 *models.ChannelEdgePolicy
+	if !reflect.DeepEqual(edges[1], models.ChannelEdgePolicy{}) {
 		edge2 = &edges[1]
 	}
 
@@ -359,12 +355,12 @@ func (r *mockGraphSource) IsStaleEdgePolicy(chanID lnwire.ShortChannelID,
 
 	switch {
 	case flags&lnwire.ChanUpdateDirection == 0 &&
-		!reflect.DeepEqual(edges[0], channeldb.ChannelEdgePolicy{}):
+		!reflect.DeepEqual(edges[0], models.ChannelEdgePolicy{}):
 
 		return !timestamp.After(edges[0].LastUpdate)
 
 	case flags&lnwire.ChanUpdateDirection == 1 &&
-		!reflect.DeepEqual(edges[1], channeldb.ChannelEdgePolicy{}):
+		!reflect.DeepEqual(edges[1], models.ChannelEdgePolicy{}):
 
 		return !timestamp.After(edges[1].LastUpdate)
 
@@ -3443,8 +3439,8 @@ out:
 	var edgesToUpdate []EdgeWithInfo
 	err = ctx.router.ForAllOutgoingChannels(func(
 		_ kvdb.RTx,
-		info *channeldb.ChannelEdgeInfo,
-		edge *channeldb.ChannelEdgePolicy) error {
+		info *models.ChannelEdgeInfo,
+		edge *models.ChannelEdgePolicy) error {
 
 		edge.TimeLockDelta = uint16(newTimeLockDelta)
 		edgesToUpdate = append(edgesToUpdate, EdgeWithInfo{
diff --git a/funding/manager_test.go b/funding/manager_test.go
index 2c77d377e..d6a0a4427 100644
--- a/funding/manager_test.go
+++ b/funding/manager_test.go
@@ -550,7 +550,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
 		OpenChannelPredicate:          chainedAcceptor,
 		NotifyPendingOpenChannelEvent: evt.NotifyPendingOpenChannelEvent,
 		DeleteAliasEdge: func(scid lnwire.ShortChannelID) (
-			*channeldb.ChannelEdgePolicy, error) {
+			*models.ChannelEdgePolicy, error) {
 
 			return nil, nil
 		},
diff --git a/lnrpc/invoicesrpc/addinvoice_test.go b/lnrpc/invoicesrpc/addinvoice_test.go
index 2dcb516a6..1b15921d0 100644
--- a/lnrpc/invoicesrpc/addinvoice_test.go
+++ b/lnrpc/invoicesrpc/addinvoice_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/zpay32"
 	"github.com/stretchr/testify/mock"
@@ -15,9 +16,14 @@ import (
 )
 
 type hopHintsConfigMock struct {
+	t *testing.T
 	mock.Mock
 }
 
+func newHopHintsConfigMock(t *testing.T) *hopHintsConfigMock {
+	return &hopHintsConfigMock{t: t}
+}
+
 // IsPublicNode mocks node public state lookup.
 func (h *hopHintsConfigMock) IsPublicNode(pubKey [33]byte) (bool, error) {
 	args := h.Mock.Called(pubKey)
@@ -51,8 +57,8 @@ func (h *hopHintsConfigMock) FetchAllChannels() ([]*channeldb.OpenChannel,
 // FetchChannelEdgesByID attempts to lookup the two directed edges for
 // the channel identified by the channel ID.
 func (h *hopHintsConfigMock) FetchChannelEdgesByID(chanID uint64) (
-	*channeldb.ChannelEdgeInfo, *channeldb.ChannelEdgePolicy,
-	*channeldb.ChannelEdgePolicy, error) {
+	*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+	*models.ChannelEdgePolicy, error) {
 
 	args := h.Mock.Called(chanID)
 
@@ -64,9 +70,14 @@ func (h *hopHintsConfigMock) FetchChannelEdgesByID(chanID uint64) (
 		return nil, nil, nil, err
 	}
 
-	edgeInfo := args.Get(0).(*channeldb.ChannelEdgeInfo)
-	policy1 := args.Get(1).(*channeldb.ChannelEdgePolicy)
-	policy2 := args.Get(2).(*channeldb.ChannelEdgePolicy)
+	edgeInfo, ok := args.Get(0).(*models.ChannelEdgeInfo)
+	require.True(h.t, ok)
+
+	policy1, ok := args.Get(1).(*models.ChannelEdgePolicy)
+	require.True(h.t, ok)
+
+	policy2, ok := args.Get(2).(*models.ChannelEdgePolicy)
+	require.True(h.t, ok)
 
 	return edgeInfo, policy1, policy2, err
 }
@@ -215,9 +226,9 @@ var shouldIncludeChannelTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 
 		h.Mock.On(
@@ -253,9 +264,9 @@ var shouldIncludeChannelTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 		alias := lnwire.ShortChannelID{TxPosition: 5}
 		h.Mock.On(
@@ -294,15 +305,15 @@ var shouldIncludeChannelTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{
+			&models.ChannelEdgeInfo{
 				NodeKey1Bytes: selectedPolicy,
 			},
-			&channeldb.ChannelEdgePolicy{
+			&models.ChannelEdgePolicy{
 				FeeBaseMSat:               1000,
 				FeeProportionalMillionths: 20,
 				TimeLockDelta:             13,
 			},
-			&channeldb.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{},
 			nil,
 		)
 	},
@@ -342,9 +353,9 @@ var shouldIncludeChannelTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{
 				FeeBaseMSat:               1000,
 				FeeProportionalMillionths: 20,
 				TimeLockDelta:             13,
@@ -387,9 +398,9 @@ var shouldIncludeChannelTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{
 				FeeBaseMSat:               1000,
 				FeeProportionalMillionths: 20,
 				TimeLockDelta:             13,
@@ -428,7 +439,7 @@ func TestShouldIncludeChannel(t *testing.T) {
 			t.Parallel()
 
 			// Create mock and prime it for the test case.
-			mock := &hopHintsConfigMock{}
+			mock := newHopHintsConfigMock(t)
 			if tc.setupMock != nil {
 				tc.setupMock(mock)
 			}
@@ -554,9 +565,9 @@ var populateHopHintsTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 	},
 	maxHopHints: 1,
@@ -604,9 +615,9 @@ var populateHopHintsTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 	},
 	maxHopHints: 10,
@@ -655,9 +666,9 @@ var populateHopHintsTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 	},
 	maxHopHints: 1,
@@ -688,9 +699,9 @@ var populateHopHintsTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 
 		// Prepare the mock for the second channel.
@@ -705,9 +716,9 @@ var populateHopHintsTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 	},
 	maxHopHints: 10,
@@ -742,9 +753,9 @@ var populateHopHintsTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 
 		// Prepare the mock for the second channel.
@@ -759,9 +770,9 @@ var populateHopHintsTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 	},
 	maxHopHints: 10,
@@ -797,9 +808,9 @@ var populateHopHintsTestCases = []struct {
 		h.Mock.On(
 			"FetchChannelEdgesByID", mock.Anything,
 		).Once().Return(
-			&channeldb.ChannelEdgeInfo{},
-			&channeldb.ChannelEdgePolicy{},
-			&channeldb.ChannelEdgePolicy{}, nil,
+			&models.ChannelEdgeInfo{},
+			&models.ChannelEdgePolicy{},
+			&models.ChannelEdgePolicy{}, nil,
 		)
 	},
 	maxHopHints: 1,
@@ -861,7 +872,7 @@ func TestPopulateHopHints(t *testing.T) {
 			t.Parallel()
 
 			// Create mock and prime it for the test case.
-			mock := &hopHintsConfigMock{}
+			mock := newHopHintsConfigMock(t)
 			if tc.setupMock != nil {
 				tc.setupMock(mock)
 			}
diff --git a/netann/chan_status_manager_test.go b/netann/chan_status_manager_test.go
index e068184f8..cb871ba01 100644
--- a/netann/chan_status_manager_test.go
+++ b/netann/chan_status_manager_test.go
@@ -14,6 +14,7 @@ import (
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/netann"
@@ -65,8 +66,8 @@ func createChannel(t *testing.T) *channeldb.OpenChannel {
 // our `pubkey` with the direction bit set appropriately in the policies. Our
 // update will be created with the disabled bit set if startEnabled is false.
 func createEdgePolicies(t *testing.T, channel *channeldb.OpenChannel,
-	pubkey *btcec.PublicKey, startEnabled bool) (*channeldb.ChannelEdgeInfo,
-	*channeldb.ChannelEdgePolicy, *channeldb.ChannelEdgePolicy) {
+	pubkey *btcec.PublicKey, startEnabled bool) (*models.ChannelEdgeInfo,
+	*models.ChannelEdgePolicy, *models.ChannelEdgePolicy) {
 
 	var (
 		pubkey1 [33]byte
@@ -98,18 +99,18 @@ func createEdgePolicies(t *testing.T, channel *channeldb.OpenChannel,
 	// bit.
 	dir2 |= lnwire.ChanUpdateDirection
 
-	return &channeldb.ChannelEdgeInfo{
+	return &models.ChannelEdgeInfo{
 			ChannelPoint:  channel.FundingOutpoint,
 			NodeKey1Bytes: pubkey1,
 			NodeKey2Bytes: pubkey2,
 		},
-		&channeldb.ChannelEdgePolicy{
+		&models.ChannelEdgePolicy{
 			ChannelID:    channel.ShortChanID().ToUint64(),
 			ChannelFlags: dir1,
 			LastUpdate:   time.Now(),
 			SigBytes:     testSigBytes,
 		},
-		&channeldb.ChannelEdgePolicy{
+		&models.ChannelEdgePolicy{
 			ChannelID:    channel.ShortChanID().ToUint64(),
 			ChannelFlags: dir2,
 			LastUpdate:   time.Now(),
@@ -120,9 +121,9 @@ func createEdgePolicies(t *testing.T, channel *channeldb.OpenChannel,
 type mockGraph struct {
 	mu        sync.Mutex
 	channels  []*channeldb.OpenChannel
-	chanInfos map[wire.OutPoint]*channeldb.ChannelEdgeInfo
-	chanPols1 map[wire.OutPoint]*channeldb.ChannelEdgePolicy
-	chanPols2 map[wire.OutPoint]*channeldb.ChannelEdgePolicy
+	chanInfos map[wire.OutPoint]*models.ChannelEdgeInfo
+	chanPols1 map[wire.OutPoint]*models.ChannelEdgePolicy
+	chanPols2 map[wire.OutPoint]*models.ChannelEdgePolicy
 	sidToCid  map[lnwire.ShortChannelID]wire.OutPoint
 
 	updates chan *lnwire.ChannelUpdate
@@ -133,9 +134,9 @@ func newMockGraph(t *testing.T, numChannels int,
 
 	g := &mockGraph{
 		channels:  make([]*channeldb.OpenChannel, 0, numChannels),
-		chanInfos: make(map[wire.OutPoint]*channeldb.ChannelEdgeInfo),
-		chanPols1: make(map[wire.OutPoint]*channeldb.ChannelEdgePolicy),
-		chanPols2: make(map[wire.OutPoint]*channeldb.ChannelEdgePolicy),
+		chanInfos: make(map[wire.OutPoint]*models.ChannelEdgeInfo),
+		chanPols1: make(map[wire.OutPoint]*models.ChannelEdgePolicy),
+		chanPols2: make(map[wire.OutPoint]*models.ChannelEdgePolicy),
 		sidToCid:  make(map[lnwire.ShortChannelID]wire.OutPoint),
 		updates:   make(chan *lnwire.ChannelUpdate, 2*numChannels),
 	}
@@ -159,8 +160,8 @@ func (g *mockGraph) FetchAllOpenChannels() ([]*channeldb.OpenChannel, error) {
 }
 
 func (g *mockGraph) FetchChannelEdgesByOutpoint(
-	op *wire.OutPoint) (*channeldb.ChannelEdgeInfo,
-	*channeldb.ChannelEdgePolicy, *channeldb.ChannelEdgePolicy, error) {
+	op *wire.OutPoint) (*models.ChannelEdgeInfo,
+	*models.ChannelEdgePolicy, *models.ChannelEdgePolicy, error) {
 
 	g.mu.Lock()
 	defer g.mu.Unlock()
@@ -209,7 +210,7 @@ func (g *mockGraph) ApplyChannelUpdate(update *lnwire.ChannelUpdate,
 
 	timestamp := time.Unix(int64(update.Timestamp), 0)
 
-	policy := &channeldb.ChannelEdgePolicy{
+	policy := &models.ChannelEdgePolicy{
 		ChannelID:    update.ShortChannelID.ToUint64(),
 		ChannelFlags: update.ChannelFlags,
 		LastUpdate:   timestamp,
@@ -247,8 +248,8 @@ func (g *mockGraph) addChannel(channel *channeldb.OpenChannel) {
 }
 
 func (g *mockGraph) addEdgePolicy(c *channeldb.OpenChannel,
-	info *channeldb.ChannelEdgeInfo,
-	pol1, pol2 *channeldb.ChannelEdgePolicy) {
+	info *models.ChannelEdgeInfo,
+	pol1, pol2 *models.ChannelEdgePolicy) {
 
 	g.mu.Lock()
 	defer g.mu.Unlock()
diff --git a/netann/channel_announcement_test.go b/netann/channel_announcement_test.go
index bc2460b9a..92a6c74ce 100644
--- a/netann/channel_announcement_test.go
+++ b/netann/channel_announcement_test.go
@@ -7,7 +7,7 @@ import (
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/wire"
-	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -39,13 +39,13 @@ func TestCreateChanAnnouncement(t *testing.T) {
 		ExtraOpaqueData: []byte{0x1},
 	}
 
-	chanProof := &channeldb.ChannelAuthProof{
+	chanProof := &models.ChannelAuthProof{
 		NodeSig1Bytes:    expChanAnn.NodeSig1.ToSignatureBytes(),
 		NodeSig2Bytes:    expChanAnn.NodeSig2.ToSignatureBytes(),
 		BitcoinSig1Bytes: expChanAnn.BitcoinSig1.ToSignatureBytes(),
 		BitcoinSig2Bytes: expChanAnn.BitcoinSig2.ToSignatureBytes(),
 	}
-	chanInfo := &channeldb.ChannelEdgeInfo{
+	chanInfo := &models.ChannelEdgeInfo{
 		ChainHash:        expChanAnn.ChainHash,
 		ChannelID:        expChanAnn.ShortChannelID.ToUint64(),
 		ChannelPoint:     wire.OutPoint{Index: 1},
diff --git a/routing/localchans/manager_test.go b/routing/localchans/manager_test.go
index b14093a01..b103ef31f 100644
--- a/routing/localchans/manager_test.go
+++ b/routing/localchans/manager_test.go
@@ -22,7 +22,7 @@ func TestManager(t *testing.T) {
 	t.Parallel()
 
 	type channel struct {
-		edgeInfo *channeldb.ChannelEdgeInfo
+		edgeInfo *models.ChannelEdgeInfo
 	}
 
 	var (
@@ -44,7 +44,7 @@ func TestManager(t *testing.T) {
 		MaxHTLC:       5000,
 	}
 
-	currentPolicy := channeldb.ChannelEdgePolicy{
+	currentPolicy := models.ChannelEdgePolicy{
 		MinHTLC:      minHTLC,
 		MessageFlags: lnwire.ChanUpdateRequiredMaxHtlc,
 	}
@@ -107,8 +107,8 @@ func TestManager(t *testing.T) {
 	}
 
 	forAllOutgoingChannels := func(cb func(kvdb.RTx,
-		*channeldb.ChannelEdgeInfo,
-		*channeldb.ChannelEdgePolicy) error) error {
+		*models.ChannelEdgeInfo,
+		*models.ChannelEdgePolicy) error) error {
 
 		for _, c := range channelSet {
 			if err := cb(nil, c.edgeInfo, &currentPolicy); err != nil {
@@ -152,7 +152,7 @@ func TestManager(t *testing.T) {
 
 	tests := []struct {
 		name                   string
-		currentPolicy          channeldb.ChannelEdgePolicy
+		currentPolicy          models.ChannelEdgePolicy
 		newPolicy              routing.ChannelPolicy
 		channelSet             []channel
 		specifiedChanPoints    []wire.OutPoint
@@ -166,7 +166,7 @@ func TestManager(t *testing.T) {
 			newPolicy:     newPolicy,
 			channelSet: []channel{
 				{
-					edgeInfo: &channeldb.ChannelEdgeInfo{
+					edgeInfo: &models.ChannelEdgeInfo{
 						Capacity:     chanCap,
 						ChannelPoint: chanPointValid,
 					},
@@ -183,7 +183,7 @@ func TestManager(t *testing.T) {
 			newPolicy:     newPolicy,
 			channelSet: []channel{
 				{
-					edgeInfo: &channeldb.ChannelEdgeInfo{
+					edgeInfo: &models.ChannelEdgeInfo{
 						Capacity:     chanCap,
 						ChannelPoint: chanPointValid,
 					},
@@ -200,7 +200,7 @@ func TestManager(t *testing.T) {
 			newPolicy:     newPolicy,
 			channelSet: []channel{
 				{
-					edgeInfo: &channeldb.ChannelEdgeInfo{
+					edgeInfo: &models.ChannelEdgeInfo{
 						Capacity:     chanCap,
 						ChannelPoint: chanPointValid,
 					},
@@ -221,7 +221,7 @@ func TestManager(t *testing.T) {
 			newPolicy:     noMaxHtlcPolicy,
 			channelSet: []channel{
 				{
-					edgeInfo: &channeldb.ChannelEdgeInfo{
+					edgeInfo: &models.ChannelEdgeInfo{
 						Capacity:     chanCap,
 						ChannelPoint: chanPointValid,
 					},
diff --git a/routing/mock_graph_test.go b/routing/mock_graph_test.go
index dfbfd6aba..2ec9a0f98 100644
--- a/routing/mock_graph_test.go
+++ b/routing/mock_graph_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
 )
@@ -193,7 +194,7 @@ func (m *mockGraph) forEachNodeChannel(nodePub route.Vertex,
 				OtherNode:    peer,
 				Capacity:     channel.capacity,
 				OutPolicySet: true,
-				InPolicy: &channeldb.CachedEdgePolicy{
+				InPolicy: &models.CachedEdgePolicy{
 					ChannelID: channel.id,
 					ToNodePubKey: func() route.Vertex {
 						return nodePub
diff --git a/routing/mock_test.go b/routing/mock_test.go
index c24ca2508..6ab8f7083 100644
--- a/routing/mock_test.go
+++ b/routing/mock_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/go-errors/errors"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/htlcswitch"
 	"github.com/lightningnetwork/lnd/lntypes"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -174,13 +175,13 @@ func (m *mockPaymentSessionOld) RequestRoute(_, _ lnwire.MilliSatoshi,
 }
 
 func (m *mockPaymentSessionOld) UpdateAdditionalEdge(_ *lnwire.ChannelUpdate,
-	_ *btcec.PublicKey, _ *channeldb.CachedEdgePolicy) bool {
+	_ *btcec.PublicKey, _ *models.CachedEdgePolicy) bool {
 
 	return false
 }
 
 func (m *mockPaymentSessionOld) GetAdditionalEdgePolicy(_ *btcec.PublicKey,
-	_ uint64) *channeldb.CachedEdgePolicy {
+	_ uint64) *models.CachedEdgePolicy {
 
 	return nil
 }
@@ -676,17 +677,17 @@ func (m *mockPaymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
 }
 
 func (m *mockPaymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
-	pubKey *btcec.PublicKey, policy *channeldb.CachedEdgePolicy) bool {
+	pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool {
 
 	args := m.Called(msg, pubKey, policy)
 	return args.Bool(0)
 }
 
 func (m *mockPaymentSession) GetAdditionalEdgePolicy(pubKey *btcec.PublicKey,
-	channelID uint64) *channeldb.CachedEdgePolicy {
+	channelID uint64) *models.CachedEdgePolicy {
 
 	args := m.Called(pubKey, channelID)
-	return args.Get(0).(*channeldb.CachedEdgePolicy)
+	return args.Get(0).(*models.CachedEdgePolicy)
 }
 
 type mockControlTower struct {
diff --git a/routing/notifications_test.go b/routing/notifications_test.go
index 00bc29f6b..5d6695010 100644
--- a/routing/notifications_test.go
+++ b/routing/notifications_test.go
@@ -16,6 +16,7 @@ import (
 	"github.com/btcsuite/btcd/wire"
 	"github.com/go-errors/errors"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/input"
 	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
@@ -74,9 +75,9 @@ func createTestNode() (*channeldb.LightningNode, error) {
 }
 
 func randEdgePolicy(chanID *lnwire.ShortChannelID,
-	node *channeldb.LightningNode) *channeldb.ChannelEdgePolicy {
+	node *channeldb.LightningNode) *models.ChannelEdgePolicy {
 
-	return &channeldb.ChannelEdgePolicy{
+	return &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 chanID.ToUint64(),
 		LastUpdate:                time.Unix(int64(prand.Int31()), 0),
@@ -85,7 +86,7 @@ func randEdgePolicy(chanID *lnwire.ShortChannelID,
 		MaxHTLC:                   lnwire.MilliSatoshi(prand.Int31()),
 		FeeBaseMSat:               lnwire.MilliSatoshi(prand.Int31()),
 		FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int31()),
-		Node:                      node,
+		ToNode:                    node.PubKeyBytes,
 	}
 }
 
@@ -431,11 +432,11 @@ func TestEdgeUpdateNotification(t *testing.T) {
 
 	// Finally, to conclude our test set up, we'll create a channel
 	// update to announce the created channel between the two nodes.
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:     chanID.ToUint64(),
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -469,7 +470,8 @@ func TestEdgeUpdateNotification(t *testing.T) {
 	}
 
 	assertEdgeCorrect := func(t *testing.T, edgeUpdate *ChannelEdgeUpdate,
-		edgeAnn *channeldb.ChannelEdgePolicy) {
+		edgeAnn *models.ChannelEdgePolicy) {
+
 		if edgeUpdate.ChanID != edgeAnn.ChannelID {
 			t.Fatalf("channel ID of edge doesn't match: "+
 				"expected %v, got %v", chanID.ToUint64(), edgeUpdate.ChanID)
@@ -613,11 +615,11 @@ func TestNodeUpdateNotification(t *testing.T) {
 	testFeaturesBuf := new(bytes.Buffer)
 	require.NoError(t, testFeatures.Encode(testFeaturesBuf))
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:     chanID.ToUint64(),
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -799,11 +801,11 @@ func TestNotificationCancellation(t *testing.T) {
 	// to the client.
 	ntfnClient.Cancel()
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:     chanID.ToUint64(),
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -870,11 +872,11 @@ func TestChannelCloseNotification(t *testing.T) {
 
 	// Finally, to conclude our test set up, we'll create a channel
 	// announcement to announce the created channel between the two nodes.
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:     chanID.ToUint64(),
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go
index c016451bd..a0dd1fa66 100644
--- a/routing/pathfind_test.go
+++ b/routing/pathfind_test.go
@@ -23,6 +23,7 @@ import (
 	"github.com/btcsuite/btcd/wire"
 	sphinx "github.com/lightningnetwork/lightning-onion"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/htlcswitch"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -96,7 +97,7 @@ var (
 	_             = testSScalar.SetByteSlice(testSBytes)
 	testSig       = ecdsa.NewSignature(testRScalar, testSScalar)
 
-	testAuthProof = channeldb.ChannelAuthProof{
+	testAuthProof = models.ChannelAuthProof{
 		NodeSig1Bytes:    testSig.Serialize(),
 		NodeSig2Bytes:    testSig.Serialize(),
 		BitcoinSig1Bytes: testSig.Serialize(),
@@ -303,16 +304,6 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
 		}
 	}
 
-	aliasForNode := func(node route.Vertex) string {
-		for alias, pubKey := range aliasMap {
-			if pubKey == node {
-				return alias
-			}
-		}
-
-		return ""
-	}
-
 	// With all the vertexes inserted, we can now insert the edges into the
 	// test graph.
 	for _, edge := range g.Edges {
@@ -345,7 +336,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
 
 		// We first insert the existence of the edge between the two
 		// nodes.
-		edgeInfo := channeldb.ChannelEdgeInfo{
+		edgeInfo := models.ChannelEdgeInfo{
 			ChannelID:    edge.ChannelID,
 			AuthProof:    &testAuthProof,
 			ChannelPoint: fundingPoint,
@@ -376,7 +367,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
 			targetNode = edgeInfo.NodeKey2Bytes
 		}
 
-		edgePolicy := &channeldb.ChannelEdgePolicy{
+		edgePolicy := &models.ChannelEdgePolicy{
 			SigBytes:                  testSig.Serialize(),
 			MessageFlags:              lnwire.ChanUpdateMsgFlags(edge.MessageFlags),
 			ChannelFlags:              channelFlags,
@@ -387,10 +378,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
 			MaxHTLC:                   lnwire.MilliSatoshi(edge.MaxHTLC),
 			FeeBaseMSat:               lnwire.MilliSatoshi(edge.FeeBaseMsat),
 			FeeProportionalMillionths: lnwire.MilliSatoshi(edge.FeeRate),
-			Node: &channeldb.LightningNode{
-				Alias:       aliasForNode(targetNode),
-				PubKeyBytes: targetNode,
-			},
+			ToNode:                    targetNode,
 		}
 		if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
 			return nil, err
@@ -657,7 +645,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 
 		// We first insert the existence of the edge between the two
 		// nodes.
-		edgeInfo := channeldb.ChannelEdgeInfo{
+		edgeInfo := models.ChannelEdgeInfo{
 			ChannelID:    channelID,
 			AuthProof:    &testAuthProof,
 			ChannelPoint: *fundingPoint,
@@ -684,12 +672,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 				channelFlags |= lnwire.ChanUpdateDisabled
 			}
 
-			node2Features := lnwire.EmptyFeatureVector()
-			if node2.testChannelPolicy != nil {
-				node2Features = node2.Features
-			}
-
-			edgePolicy := &channeldb.ChannelEdgePolicy{
+			edgePolicy := &models.ChannelEdgePolicy{
 				SigBytes:                  testSig.Serialize(),
 				MessageFlags:              msgFlags,
 				ChannelFlags:              channelFlags,
@@ -700,11 +683,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 				MaxHTLC:                   node1.MaxHTLC,
 				FeeBaseMSat:               node1.FeeBaseMsat,
 				FeeProportionalMillionths: node1.FeeRate,
-				Node: &channeldb.LightningNode{
-					Alias:       node2.Alias,
-					PubKeyBytes: node2Vertex,
-					Features:    node2Features,
-				},
+				ToNode:                    node2Vertex,
 			}
 			if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
 				return nil, err
@@ -722,12 +701,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 			}
 			channelFlags |= lnwire.ChanUpdateDirection
 
-			node1Features := lnwire.EmptyFeatureVector()
-			if node1.testChannelPolicy != nil {
-				node1Features = node1.Features
-			}
-
-			edgePolicy := &channeldb.ChannelEdgePolicy{
+			edgePolicy := &models.ChannelEdgePolicy{
 				SigBytes:                  testSig.Serialize(),
 				MessageFlags:              msgFlags,
 				ChannelFlags:              channelFlags,
@@ -738,11 +712,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 				MaxHTLC:                   node2.MaxHTLC,
 				FeeBaseMSat:               node2.FeeBaseMsat,
 				FeeProportionalMillionths: node2.FeeRate,
-				Node: &channeldb.LightningNode{
-					Alias:       node1.Alias,
-					PubKeyBytes: node1Vertex,
-					Features:    node1Features,
-				},
+				ToNode:                    node1Vertex,
 			}
 			if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
 				return nil, err
@@ -1234,7 +1204,7 @@ func runPathFindingWithAdditionalEdges(t *testing.T, useCache bool) {
 
 	// Create the channel edge going from songoku to doge and include it in
 	// our map of additional edges.
-	songokuToDoge := &channeldb.CachedEdgePolicy{
+	songokuToDoge := &models.CachedEdgePolicy{
 		ToNodePubKey: func() route.Vertex {
 			return doge.PubKeyBytes
 		},
@@ -1245,12 +1215,12 @@ func runPathFindingWithAdditionalEdges(t *testing.T, useCache bool) {
 		TimeLockDelta:             9,
 	}
 
-	additionalEdges := map[route.Vertex][]*channeldb.CachedEdgePolicy{
+	additionalEdges := map[route.Vertex][]*models.CachedEdgePolicy{
 		graph.aliasMap["songoku"]: {songokuToDoge},
 	}
 
 	find := func(r *RestrictParams) (
-		[]*channeldb.CachedEdgePolicy, error) {
+		[]*models.CachedEdgePolicy, error) {
 
 		return dbFindPath(
 			graph.graph, additionalEdges, &mockBandwidthHints{},
@@ -1320,7 +1290,7 @@ func runPathFindingWithRedundantAdditionalEdges(t *testing.T, useCache bool) {
 
 	// Create the channel edge going from alice to bob and include it in
 	// our map of additional edges.
-	aliceToBob := &channeldb.CachedEdgePolicy{
+	aliceToBob := &models.CachedEdgePolicy{
 		ToNodePubKey: func() route.Vertex {
 			return target
 		},
@@ -1331,7 +1301,7 @@ func runPathFindingWithRedundantAdditionalEdges(t *testing.T, useCache bool) {
 		TimeLockDelta:             9,
 	}
 
-	additionalEdges := map[route.Vertex][]*channeldb.CachedEdgePolicy{
+	additionalEdges := map[route.Vertex][]*models.CachedEdgePolicy{
 		ctx.source: {aliceToBob},
 	}
 
@@ -1365,9 +1335,9 @@ func TestNewRoute(t *testing.T) {
 	createHop := func(baseFee lnwire.MilliSatoshi,
 		feeRate lnwire.MilliSatoshi,
 		bandwidth lnwire.MilliSatoshi,
-		timeLockDelta uint16) *channeldb.CachedEdgePolicy {
+		timeLockDelta uint16) *models.CachedEdgePolicy {
 
-		return &channeldb.CachedEdgePolicy{
+		return &models.CachedEdgePolicy{
 			ToNodePubKey: func() route.Vertex {
 				return route.Vertex{}
 			},
@@ -1384,7 +1354,7 @@ func TestNewRoute(t *testing.T) {
 
 		// hops is the list of hops (the route) that gets passed into
 		// the call to newRoute.
-		hops []*channeldb.CachedEdgePolicy
+		hops []*models.CachedEdgePolicy
 
 		// paymentAmount is the amount that is send into the route
 		// indicated by hops.
@@ -1436,7 +1406,7 @@ func TestNewRoute(t *testing.T) {
 			// For a single hop payment, no fees are expected to be paid.
 			name:          "single hop",
 			paymentAmount: 100000,
-			hops: []*channeldb.CachedEdgePolicy{
+			hops: []*models.CachedEdgePolicy{
 				createHop(100, 1000, 1000000, 10),
 			},
 			metadata:              []byte{1, 2, 3},
@@ -1450,7 +1420,7 @@ func TestNewRoute(t *testing.T) {
 			// a fee to receive the payment.
 			name:          "two hop",
 			paymentAmount: 100000,
-			hops: []*channeldb.CachedEdgePolicy{
+			hops: []*models.CachedEdgePolicy{
 				createHop(0, 1000, 1000000, 10),
 				createHop(30, 1000, 1000000, 5),
 			},
@@ -1465,7 +1435,7 @@ func TestNewRoute(t *testing.T) {
 			name:          "two hop tlv onion feature",
 			destFeatures:  tlvFeatures,
 			paymentAmount: 100000,
-			hops: []*channeldb.CachedEdgePolicy{
+			hops: []*models.CachedEdgePolicy{
 				createHop(0, 1000, 1000000, 10),
 				createHop(30, 1000, 1000000, 5),
 			},
@@ -1482,7 +1452,7 @@ func TestNewRoute(t *testing.T) {
 			destFeatures:  tlvPayAddrFeatures,
 			paymentAddr:   &testPaymentAddr,
 			paymentAmount: 100000,
-			hops: []*channeldb.CachedEdgePolicy{
+			hops: []*models.CachedEdgePolicy{
 				createHop(0, 1000, 1000000, 10),
 				createHop(30, 1000, 1000000, 5),
 			},
@@ -1502,7 +1472,7 @@ func TestNewRoute(t *testing.T) {
 			// gets rounded down to 1.
 			name:          "three hop",
 			paymentAmount: 100000,
-			hops: []*channeldb.CachedEdgePolicy{
+			hops: []*models.CachedEdgePolicy{
 				createHop(0, 10, 1000000, 10),
 				createHop(0, 10, 1000000, 5),
 				createHop(0, 10, 1000000, 3),
@@ -1517,7 +1487,7 @@ func TestNewRoute(t *testing.T) {
 			// because of the increase amount to forward.
 			name:          "three hop with fee carry over",
 			paymentAmount: 100000,
-			hops: []*channeldb.CachedEdgePolicy{
+			hops: []*models.CachedEdgePolicy{
 				createHop(0, 10000, 1000000, 10),
 				createHop(0, 10000, 1000000, 5),
 				createHop(0, 10000, 1000000, 3),
@@ -1532,7 +1502,7 @@ func TestNewRoute(t *testing.T) {
 			// effect.
 			name:          "three hop with minimal fees for carry over",
 			paymentAmount: 100000,
-			hops: []*channeldb.CachedEdgePolicy{
+			hops: []*models.CachedEdgePolicy{
 				createHop(0, 10000, 1000000, 10),
 
 				// First hop charges 0.1% so the second hop fee
@@ -1771,7 +1741,7 @@ func runDestTLVGraphFallback(t *testing.T, useCache bool) {
 	require.NoError(t, err, "unable to fetch source node")
 
 	find := func(r *RestrictParams,
-		target route.Vertex) ([]*channeldb.CachedEdgePolicy, error) {
+		target route.Vertex) ([]*models.CachedEdgePolicy, error) {
 
 		return dbFindPath(
 			ctx.graph, nil, &mockBandwidthHints{},
@@ -2283,7 +2253,7 @@ func TestPathFindSpecExample(t *testing.T) {
 	// Carol, so we set "B" as the source node so path finding starts from
 	// Bob.
 	bob := ctx.aliases["B"]
-	bobNode, err := ctx.graph.FetchLightningNode(bob)
+	bobNode, err := ctx.graph.FetchLightningNode(nil, bob)
 	require.NoError(t, err, "unable to find bob")
 	if err := ctx.graph.SetSourceNode(bobNode); err != nil {
 		t.Fatalf("unable to set source node: %v", err)
@@ -2332,7 +2302,7 @@ func TestPathFindSpecExample(t *testing.T) {
 	// Next, we'll set A as the source node so we can assert that we create
 	// the proper route for any queries starting with Alice.
 	alice := ctx.aliases["A"]
-	aliceNode, err := ctx.graph.FetchLightningNode(alice)
+	aliceNode, err := ctx.graph.FetchLightningNode(nil, alice)
 	require.NoError(t, err, "unable to find alice")
 	if err := ctx.graph.SetSourceNode(aliceNode); err != nil {
 		t.Fatalf("unable to set source node: %v", err)
@@ -2429,7 +2399,7 @@ func TestPathFindSpecExample(t *testing.T) {
 }
 
 func assertExpectedPath(t *testing.T, aliasMap map[string]route.Vertex,
-	path []*channeldb.CachedEdgePolicy, nodeAliases ...string) {
+	path []*models.CachedEdgePolicy, nodeAliases ...string) {
 
 	if len(path) != len(nodeAliases) {
 		t.Fatal("number of hops and number of aliases do not match")
@@ -3074,7 +3044,7 @@ func (c *pathFindingTestContext) aliasFromKey(pubKey route.Vertex) string {
 }
 
 func (c *pathFindingTestContext) findPath(target route.Vertex,
-	amt lnwire.MilliSatoshi) ([]*channeldb.CachedEdgePolicy,
+	amt lnwire.MilliSatoshi) ([]*models.CachedEdgePolicy,
 	error) {
 
 	return dbFindPath(
@@ -3083,7 +3053,7 @@ func (c *pathFindingTestContext) findPath(target route.Vertex,
 	)
 }
 
-func (c *pathFindingTestContext) assertPath(path []*channeldb.CachedEdgePolicy,
+func (c *pathFindingTestContext) assertPath(path []*models.CachedEdgePolicy,
 	expected []uint64) {
 
 	if len(path) != len(expected) {
@@ -3102,11 +3072,11 @@ func (c *pathFindingTestContext) assertPath(path []*channeldb.CachedEdgePolicy,
 // dbFindPath calls findPath after getting a db transaction from the database
 // graph.
 func dbFindPath(graph *channeldb.ChannelGraph,
-	additionalEdges map[route.Vertex][]*channeldb.CachedEdgePolicy,
+	additionalEdges map[route.Vertex][]*models.CachedEdgePolicy,
 	bandwidthHints bandwidthHints,
 	r *RestrictParams, cfg *PathFindingConfig,
 	source, target route.Vertex, amt lnwire.MilliSatoshi, timePref float64,
-	finalHtlcExpiry int32) ([]*channeldb.CachedEdgePolicy, error) {
+	finalHtlcExpiry int32) ([]*models.CachedEdgePolicy, error) {
 
 	sourceNode, err := graph.SourceNode()
 	if err != nil {
@@ -3210,7 +3180,7 @@ func TestBlindedRouteConstruction(t *testing.T) {
 		// route. Proportional fees are omitted for easy test
 		// calculations, but non-zero base fees ensure our fee is
 		// still accounted for.
-		aliceBobEdge = &channeldb.CachedEdgePolicy{
+		aliceBobEdge = &models.CachedEdgePolicy{
 			ChannelID: 1,
 			// We won't actually use this timelock / fee (since
 			// it's the sender's outbound channel), but we include
@@ -3224,7 +3194,7 @@ func TestBlindedRouteConstruction(t *testing.T) {
 			ToNodeFeatures: tlvFeatures,
 		}
 
-		bobCarolEdge = &channeldb.CachedEdgePolicy{
+		bobCarolEdge = &models.CachedEdgePolicy{
 			ChannelID:     2,
 			TimeLockDelta: 15,
 			FeeBaseMSat:   20,
@@ -3257,7 +3227,7 @@ func TestBlindedRouteConstruction(t *testing.T) {
 	carolDaveEdge := blindedEdges[carolVertex][0]
 	daveEveEdge := blindedEdges[daveBlindedVertex][0]
 
-	edges := []*channeldb.CachedEdgePolicy{
+	edges := []*models.CachedEdgePolicy{
 		aliceBobEdge,
 		bobCarolEdge,
 		carolDaveEdge,
diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go
index 18858f1f9..1c199ff40 100644
--- a/routing/payment_session_test.go
+++ b/routing/payment_session_test.go
@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lntypes"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -211,7 +212,7 @@ func TestRequestRoute(t *testing.T) {
 	// Override pathfinder with a mock.
 	session.pathFinder = func(_ *graphParams, r *RestrictParams,
 		_ *PathFindingConfig, _, _ route.Vertex, _ lnwire.MilliSatoshi,
-		_ float64, _ int32) ([]*channeldb.CachedEdgePolicy, float64,
+		_ float64, _ int32) ([]*models.CachedEdgePolicy, float64,
 		error) {
 
 		// We expect find path to receive a cltv limit excluding the
@@ -220,7 +221,7 @@ func TestRequestRoute(t *testing.T) {
 			t.Fatal("wrong cltv limit")
 		}
 
-		path := []*channeldb.CachedEdgePolicy{
+		path := []*models.CachedEdgePolicy{
 			{
 				ToNodePubKey: func() route.Vertex {
 					return route.Vertex{}
diff --git a/routing/router_test.go b/routing/router_test.go
index ea1fec287..14e2ae839 100644
--- a/routing/router_test.go
+++ b/routing/router_test.go
@@ -19,6 +19,7 @@ import (
 	sphinx "github.com/lightningnetwork/lightning-onion"
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/clock"
 	"github.com/lightningnetwork/lnd/htlcswitch"
 	lnmock "github.com/lightningnetwork/lnd/lntest/mock"
@@ -1241,7 +1242,7 @@ func TestAddProof(t *testing.T) {
 	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
 
 	// After utxo was recreated adding the edge without the proof.
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:     chanID.ToUint64(),
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
@@ -1330,7 +1331,7 @@ func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) {
 	}
 	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:        chanID.ToUint64(),
 		NodeKey1Bytes:    pub1,
 		NodeKey2Bytes:    pub2,
@@ -1338,7 +1339,7 @@ func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) {
 		BitcoinKey2Bytes: pub2,
 		AuthProof:        nil,
 	}
-	edgePolicy := &channeldb.ChannelEdgePolicy{
+	edgePolicy := &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 edge.ChannelID,
 		LastUpdate:                testTime,
@@ -1408,7 +1409,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 	}
 	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:        chanID.ToUint64(),
 		NodeKey1Bytes:    pub1,
 		NodeKey2Bytes:    pub2,
@@ -1423,7 +1424,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 
 	// We must add the edge policy to be able to use the edge for route
 	// finding.
-	edgePolicy := &channeldb.ChannelEdgePolicy{
+	edgePolicy := &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 edge.ChannelID,
 		LastUpdate:                testTime,
@@ -1431,9 +1432,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 		MinHTLC:                   1,
 		FeeBaseMSat:               10,
 		FeeProportionalMillionths: 10000,
-		Node: &channeldb.LightningNode{
-			PubKeyBytes: edge.NodeKey2Bytes,
-		},
+		ToNode:                    edge.NodeKey2Bytes,
 	}
 	edgePolicy.ChannelFlags = 0
 
@@ -1442,7 +1441,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 	}
 
 	// Create edge in the other direction as well.
-	edgePolicy = &channeldb.ChannelEdgePolicy{
+	edgePolicy = &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 edge.ChannelID,
 		LastUpdate:                testTime,
@@ -1450,9 +1449,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 		MinHTLC:                   1,
 		FeeBaseMSat:               10,
 		FeeProportionalMillionths: 10000,
-		Node: &channeldb.LightningNode{
-			PubKeyBytes: edge.NodeKey1Bytes,
-		},
+		ToNode:                    edge.NodeKey1Bytes,
 	}
 	edgePolicy.ChannelFlags = 1
 
@@ -1507,7 +1504,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 	}
 	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
 
-	edge = &channeldb.ChannelEdgeInfo{
+	edge = &models.ChannelEdgeInfo{
 		ChannelID: chanID.ToUint64(),
 		AuthProof: nil,
 	}
@@ -1520,7 +1517,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 		t.Fatalf("unable to add edge to the channel graph: %v.", err)
 	}
 
-	edgePolicy = &channeldb.ChannelEdgePolicy{
+	edgePolicy = &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 edge.ChannelID,
 		LastUpdate:                testTime,
@@ -1528,9 +1525,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 		MinHTLC:                   1,
 		FeeBaseMSat:               10,
 		FeeProportionalMillionths: 10000,
-		Node: &channeldb.LightningNode{
-			PubKeyBytes: edge.NodeKey2Bytes,
-		},
+		ToNode:                    edge.NodeKey2Bytes,
 	}
 	edgePolicy.ChannelFlags = 0
 
@@ -1538,7 +1533,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 		t.Fatalf("unable to update edge policy: %v", err)
 	}
 
-	edgePolicy = &channeldb.ChannelEdgePolicy{
+	edgePolicy = &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 edge.ChannelID,
 		LastUpdate:                testTime,
@@ -1546,9 +1541,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 		MinHTLC:                   1,
 		FeeBaseMSat:               10,
 		FeeProportionalMillionths: 10000,
-		Node: &channeldb.LightningNode{
-			PubKeyBytes: edge.NodeKey1Bytes,
-		},
+		ToNode:                    edge.NodeKey1Bytes,
 	}
 	edgePolicy.ChannelFlags = 1
 
@@ -1613,14 +1606,14 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
 	_, _, err = ctx.router.FindRoute(req)
 	require.NoError(t, err, "unable to find any routes")
 
-	copy1, err := ctx.graph.FetchLightningNode(pub1)
+	copy1, err := ctx.graph.FetchLightningNode(nil, pub1)
 	require.NoError(t, err, "unable to fetch node")
 
 	if copy1.Alias != n1.Alias {
 		t.Fatalf("fetched node not equal to original")
 	}
 
-	copy2, err := ctx.graph.FetchLightningNode(pub2)
+	copy2, err := ctx.graph.FetchLightningNode(nil, pub2)
 	require.NoError(t, err, "unable to fetch node")
 
 	if copy2.Alias != n2.Alias {
@@ -1710,11 +1703,11 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
 	node2, err := createTestNode()
 	require.NoError(t, err, "unable to create test node")
 
-	edge1 := &channeldb.ChannelEdgeInfo{
+	edge1 := &models.ChannelEdgeInfo{
 		ChannelID:     chanID1,
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -1728,11 +1721,11 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
 		t.Fatalf("unable to add edge: %v", err)
 	}
 
-	edge2 := &channeldb.ChannelEdgeInfo{
+	edge2 := &models.ChannelEdgeInfo{
 		ChannelID:     chanID2,
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -1918,13 +1911,13 @@ func TestDisconnectedBlocks(t *testing.T) {
 	node2, err := createTestNode()
 	require.NoError(t, err, "unable to create test node")
 
-	edge1 := &channeldb.ChannelEdgeInfo{
+	edge1 := &models.ChannelEdgeInfo{
 		ChannelID:        chanID1,
 		NodeKey1Bytes:    node1.PubKeyBytes,
 		NodeKey2Bytes:    node2.PubKeyBytes,
 		BitcoinKey1Bytes: node1.PubKeyBytes,
 		BitcoinKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -1938,13 +1931,13 @@ func TestDisconnectedBlocks(t *testing.T) {
 		t.Fatalf("unable to add edge: %v", err)
 	}
 
-	edge2 := &channeldb.ChannelEdgeInfo{
+	edge2 := &models.ChannelEdgeInfo{
 		ChannelID:        chanID2,
 		NodeKey1Bytes:    node1.PubKeyBytes,
 		NodeKey2Bytes:    node2.PubKeyBytes,
 		BitcoinKey1Bytes: node1.PubKeyBytes,
 		BitcoinKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -2069,11 +2062,11 @@ func TestRouterChansClosedOfflinePruneGraph(t *testing.T) {
 	require.NoError(t, err, "unable to create test node")
 	node2, err := createTestNode()
 	require.NoError(t, err, "unable to create test node")
-	edge1 := &channeldb.ChannelEdgeInfo{
+	edge1 := &models.ChannelEdgeInfo{
 		ChannelID:     chanID1.ToUint64(),
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
-		AuthProof: &channeldb.ChannelAuthProof{
+		AuthProof: &models.ChannelAuthProof{
 			NodeSig1Bytes:    testSig.Serialize(),
 			NodeSig2Bytes:    testSig.Serialize(),
 			BitcoinSig1Bytes: testSig.Serialize(),
@@ -2508,7 +2501,7 @@ func TestIsStaleNode(t *testing.T) {
 	}
 	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:        chanID.ToUint64(),
 		NodeKey1Bytes:    pub1,
 		NodeKey2Bytes:    pub2,
@@ -2584,7 +2577,7 @@ func TestIsKnownEdge(t *testing.T) {
 	}
 	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:        chanID.ToUint64(),
 		NodeKey1Bytes:    pub1,
 		NodeKey2Bytes:    pub2,
@@ -2640,7 +2633,7 @@ func TestIsStaleEdgePolicy(t *testing.T) {
 		t.Fatalf("router failed to detect fresh edge policy")
 	}
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:        chanID.ToUint64(),
 		NodeKey1Bytes:    pub1,
 		NodeKey2Bytes:    pub2,
@@ -2653,7 +2646,7 @@ func TestIsStaleEdgePolicy(t *testing.T) {
 	}
 
 	// We'll also add two edge policies, one for each direction.
-	edgePolicy := &channeldb.ChannelEdgePolicy{
+	edgePolicy := &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 edge.ChannelID,
 		LastUpdate:                updateTimeStamp,
@@ -2667,7 +2660,7 @@ func TestIsStaleEdgePolicy(t *testing.T) {
 		t.Fatalf("unable to update edge policy: %v", err)
 	}
 
-	edgePolicy = &channeldb.ChannelEdgePolicy{
+	edgePolicy = &models.ChannelEdgePolicy{
 		SigBytes:                  testSig.Serialize(),
 		ChannelID:                 edge.ChannelID,
 		LastUpdate:                updateTimeStamp,
@@ -3210,7 +3203,7 @@ func TestGetPathEdges(t *testing.T) {
 		bandwidthHints *bandwidthManager
 		hops           []route.Vertex
 
-		expectedEdges []*channeldb.CachedEdgePolicy
+		expectedEdges []*models.CachedEdgePolicy
 		expectedAmt   lnwire.MilliSatoshi
 		expectedErr   string
 	}{{
@@ -3265,7 +3258,7 @@ const (
 // newChannelEdgeInfo is a helper function used to create a new channel edge,
 // possibly skipping adding it to parts of the chain/state as well.
 func newChannelEdgeInfo(ctx *testCtx, fundingHeight uint32,
-	ecm edgeCreationModifier) (*channeldb.ChannelEdgeInfo, error) {
+	ecm edgeCreationModifier) (*models.ChannelEdgeInfo, error) {
 
 	node1, err := createTestNode()
 	if err != nil {
@@ -3284,7 +3277,7 @@ func newChannelEdgeInfo(ctx *testCtx, fundingHeight uint32,
 		return nil, fmt.Errorf("unable to create edge: %w", err)
 	}
 
-	edge := &channeldb.ChannelEdgeInfo{
+	edge := &models.ChannelEdgeInfo{
 		ChannelID:     chanID.ToUint64(),
 		NodeKey1Bytes: node1.PubKeyBytes,
 		NodeKey2Bytes: node2.PubKeyBytes,
@@ -3315,7 +3308,7 @@ func newChannelEdgeInfo(ctx *testCtx, fundingHeight uint32,
 }
 
 func assertChanChainRejection(t *testing.T, ctx *testCtx,
-	edge *channeldb.ChannelEdgeInfo, failCode errorCode) {
+	edge *models.ChannelEdgeInfo, failCode errorCode) {
 
 	t.Helper()
 
@@ -4723,7 +4716,7 @@ func TestNewRouteRequest(t *testing.T) {
 			name:           "hints and blinded",
 			blindedPayment: blindedMultiHop,
 			routeHints: make(
-				map[route.Vertex][]*channeldb.CachedEdgePolicy,
+				map[route.Vertex][]*models.CachedEdgePolicy,
 			),
 			err: ErrHintsAndBlinded,
 		},
diff --git a/routing/unified_edges_test.go b/routing/unified_edges_test.go
index 752a79718..9b603d78c 100644
--- a/routing/unified_edges_test.go
+++ b/routing/unified_edges_test.go
@@ -4,7 +4,7 @@ import (
 	"testing"
 
 	"github.com/btcsuite/btcd/btcutil"
-	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
 	"github.com/stretchr/testify/require"
@@ -21,7 +21,7 @@ func TestNodeEdgeUnifier(t *testing.T) {
 	bandwidthHints := &mockBandwidthHints{}
 
 	// Add two channels between the pair of nodes.
-	p1 := channeldb.CachedEdgePolicy{
+	p1 := models.CachedEdgePolicy{
 		FeeProportionalMillionths: 100000,
 		FeeBaseMSat:               30,
 		TimeLockDelta:             60,
@@ -29,7 +29,7 @@ func TestNodeEdgeUnifier(t *testing.T) {
 		MaxHTLC:                   5000,
 		MinHTLC:                   100,
 	}
-	p2 := channeldb.CachedEdgePolicy{
+	p2 := models.CachedEdgePolicy{
 		FeeProportionalMillionths: 190000,
 		FeeBaseMSat:               10,
 		TimeLockDelta:             40,
@@ -49,7 +49,7 @@ func TestNodeEdgeUnifier(t *testing.T) {
 	unifierNoCapacity.addPolicy(fromNode, &p2, 0)
 
 	unifierNoInfo := newNodeEdgeUnifier(source, toNode, nil)
-	unifierNoInfo.addPolicy(fromNode, &channeldb.CachedEdgePolicy{}, 0)
+	unifierNoInfo.addPolicy(fromNode, &models.CachedEdgePolicy{}, 0)
 
 	tests := []struct {
 		name             string