Repository: lightningnetwork/lnd
Pull request #9996 (2025-06-25 14:38:54)

chainreg: use new lseed service
## Change Description
This points **_lnd_** at the new deployment of the **_lseed_** service used to provide candidate peers during initial network bootstrap. We also add lightning seed hostnames for the `testnet4` network.

## Steps to Test
- Start up an lnd with this change. Or update your `lnd.conf` file to point your node at the new service as demonstrated [here](https://github.com/lightningnetwork/lnd/blob/v0.19.1-beta/sample-lnd.conf#L709).
- And/or manually interact with the **_lseed_** service:
```
# Manually query the lightning seed service:
dig nodes.lightning.wiki SRV
dig test.nodes.lightning.wiki SRV
dig test4.nodes.lightning.wiki SRV
dig signet.nodes.lightning.wiki SRV

# Obtain network address of a peer from lseed service:
dig ln1q00n7z3066l22s56t9jxrnncfjfzk2vp4ks6lzw0alxeena3dst2wxe8xyt.nodes.lightning.wiki

# Convert node hostname to node ID public key (bech32 decode):
ln1q00n7z3066l22s56t9jxrnncfjfzk2vp4ks6lzw0alxeena3dst2wxe8xyt --> 03df3f0a2fd6bea5429a596461ce784c922b2981ada1af89cfefcd9ccfb16c16a7

```

## Pull Request Checklist
### Testing
- [ ] Your PR passes all CI checks.
- [ ] Tests covering the positive and negative (error paths) are included.
- [ ] Bug fixes contain tests triggering the bug to prevent regressions.

### Code Style and Documentation
- [ ] The change is not [insubstantial](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#substantial-contributions-only). Typo fixes are not accepted to fight bot spam.
- [ ] The change obeys the [Code Documentation and Commenting](https://github.com/lightningnetwork/lnd/blob/master/docs/development_guidelines.md#code-documentation-and-commenting) guidelines, and lines wrap at 80.
- [ ] Commits follow the [Ideal Git Commit Structure](https://github.com/lightningnetwork/lnd/blob/master/docs/development_guidelines.md#ideal-git-commit-structure).
- [ ] Any new logging statements use an appropriate subsystem and logging level.
- [ ] Any new lncli commands have appropriate tags in the comments for the rpc in the proto file.
- [ ] [There is a change description in the release notes](https://github.com/lightningnetwork/lnd/tree/master/docs/release-notes), or `[skip ci]` in the commit message for small changes.

📝 Please see our [Contribution Guidelines](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md) for further guidance.



Patch:
diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go
index 3f9f738b2..6ee274d68 100644
--- a/chainreg/chainregistry.go
+++ b/chainreg/chainregistry.go
@@ -878,8 +878,8 @@ var (
 	ChainDNSSeeds = map[chainhash.Hash][][2]string{
 		BitcoinMainnetGenesis: {
 			{
-				"nodes.lightning.directory",
-				"soa.nodes.lightning.directory",
+				"nodes.lightning.wiki",
+				"soa.nodes.lightning.wiki",
 			},
 			{
 				"lseed.bitcoinstats.com",
@@ -888,14 +888,22 @@ var (
 
 		BitcoinTestnetGenesis: {
 			{
-				"test.nodes.lightning.directory",
-				"soa.nodes.lightning.directory",
+				"test.nodes.lightning.wiki",
+				"soa.nodes.lightning.wiki",
+			},
+		},
+
+		BitcoinTestnet4Genesis: {
+			{
+				"test4.nodes.lightning.wiki",
+				"soa.nodes.lightning.wiki",
 			},
 		},
 
 		BitcoinSignetGenesis: {
 			{
-				"ln.signet.secp.tech",
+				"signet.nodes.lightning.wiki",
+				"soa.nodes.lightning.wiki",
 			},
 		},
 	}
diff --git a/docs/release-notes/release-notes-0.19.2.md b/docs/release-notes/release-notes-0.19.2.md
index 4ee4b4ce5..10baf36fb 100644
--- a/docs/release-notes/release-notes-0.19.2.md
+++ b/docs/release-notes/release-notes-0.19.2.md
@@ -37,6 +37,11 @@
   `FeeForWeightRoundUp` to the `chainfee` package which rounds up a calculated 
   fee value to the nearest satoshi.
 
+- [Update](https://github.com/lightningnetwork/lnd/pull/9996) lnd to point at
+  the new deployment of the lightning seed service which is used to provide
+  candidate peers during initial network bootstrap. The lseed service now
+  supports `testnet4` and `signet` networks as well.
+
 ## RPC Additions
 
 ## lncli Additions
diff --git a/docs/release-notes/release-notes-0.20.0.md b/docs/release-notes/release-notes-0.20.0.md
index da32397a7..19cff1f38 100644
--- a/docs/release-notes/release-notes-0.20.0.md
+++ b/docs/release-notes/release-notes-0.20.0.md
@@ -37,8 +37,7 @@ circuit. The indices are only available for forwarding events saved after v0.20.
 
 
 * The `lncli addinvoice --blind` command now has the option to include a
-  chained channels [1](https://github.com/lightningnetwork/lnd/pull/9127)
-  [2](https://github.com/lightningnetwork/lnd/pull/9925)
+  [chained channels](https://github.com/lightningnetwork/lnd/pull/9127)
   incoming list `--blinded_path_incoming_channel_list` which gives users the 
   control of specifying the channels they prefer to receive the payment on. With
   the option to specify multiple channels this control can be extended to
@@ -90,7 +89,6 @@ circuit. The indices are only available for forwarding events saved after v0.20.
     * [7](https://github.com/lightningnetwork/lnd/pull/9937)
     * [8](https://github.com/lightningnetwork/lnd/pull/9938)
     * [9](https://github.com/lightningnetwork/lnd/pull/9939)
-    * [10](https://github.com/lightningnetwork/lnd/pull/9971)
 
 ## RPC Updates
 
diff --git a/graph/db/kv_store.go b/graph/db/kv_store.go
index 632ace84b..150e290ad 100644
--- a/graph/db/kv_store.go
+++ b/graph/db/kv_store.go
@@ -2150,8 +2150,8 @@ func (c *KVStore) ChanUpdatesInHorizon(startTime,
 	}
 
 	if len(edgesInHorizon) > 0 {
-		log.Debugf("ChanUpdatesInHorizon hit percentage: %.2f (%d/%d)",
-			float64(hits)*100/float64(len(edgesInHorizon)), hits,
+		log.Debugf("ChanUpdatesInHorizon hit percentage: %f (%d/%d)",
+			float64(hits)/float64(len(edgesInHorizon)), hits,
 			len(edgesInHorizon))
 	} else {
 		log.Debugf("ChanUpdatesInHorizon returned no edges in "+
diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go
index 42a5d8126..a6c67948c 100644
--- a/graph/db/sql_store.go
+++ b/graph/db/sql_store.go
@@ -91,7 +91,6 @@ type SQLQueries interface {
 		Channel queries.
 	*/
 	CreateChannel(ctx context.Context, arg sqlc.CreateChannelParams) (int64, error)
-	AddV1ChannelProof(ctx context.Context, arg sqlc.AddV1ChannelProofParams) (sql.Result, error)
 	GetChannelBySCID(ctx context.Context, arg sqlc.GetChannelBySCIDParams) (sqlc.Channel, error)
 	GetChannelByOutpoint(ctx context.Context, outpoint string) (sqlc.GetChannelByOutpointRow, error)
 	GetChannelsBySCIDRange(ctx context.Context, arg sqlc.GetChannelsBySCIDRangeParams) ([]sqlc.GetChannelsBySCIDRangeRow, error)
@@ -137,12 +136,6 @@ type SQLQueries interface {
 	GetPruneTip(ctx context.Context) (sqlc.PruneLog, error)
 	UpsertPruneLogEntry(ctx context.Context, arg sqlc.UpsertPruneLogEntryParams) error
 	DeletePruneLogEntriesInRange(ctx context.Context, arg sqlc.DeletePruneLogEntriesInRangeParams) error
-
-	/*
-		Closed SCID table queries.
-	*/
-	InsertClosedChannel(ctx context.Context, scid []byte) error
-	IsClosedChannel(ctx context.Context, scid []byte) (bool, error)
 }
 
 // BatchedSQLQueries is a version of SQLQueries that's capable of batched
@@ -1103,8 +1096,8 @@ func (s *SQLStore) ChanUpdatesInHorizon(startTime,
 	}
 
 	if len(edges) > 0 {
-		log.Debugf("ChanUpdatesInHorizon hit percentage: %.2f (%d/%d)",
-			float64(hits)*100/float64(len(edges)), hits, len(edges))
+		log.Debugf("ChanUpdatesInHorizon hit percentage: %f (%d/%d)",
+			float64(hits)/float64(len(edges)), hits, len(edges))
 	} else {
 		log.Debugf("ChanUpdatesInHorizon returned no edges in "+
 			"horizon (%s, %s)", startTime, endTime)
@@ -1238,103 +1231,6 @@ func (s *SQLStore) ForEachNodeCached(cb func(node route.Vertex,
 	}, sqldb.NoOpReset)
 }
 
-// ForEachChannelCacheable iterates through all the channel edges stored
-// within the graph and invokes the passed callback for each edge. The
-// callback takes two edges as since this is a directed graph, both the
-// in/out edges are visited. If the callback returns an error, then the
-// transaction is aborted and the iteration stops early.
-//
-// 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.
-//
-// NOTE: this method is like ForEachChannel but fetches only the data
-// required for the graph cache.
-func (s *SQLStore) ForEachChannelCacheable(cb func(*models.CachedEdgeInfo,
-	*models.CachedEdgePolicy,
-	*models.CachedEdgePolicy) error) error {
-
-	ctx := context.TODO()
-
-	handleChannel := func(db SQLQueries,
-		row sqlc.ListChannelsWithPoliciesPaginatedRow) error {
-
-		node1, node2, err := buildNodeVertices(
-			row.Node1Pubkey, row.Node2Pubkey,
-		)
-		if err != nil {
-			return err
-		}
-
-		edge := buildCacheableChannelInfo(row.Channel, node1, node2)
-
-		dbPol1, dbPol2, err := extractChannelPolicies(row)
-		if err != nil {
-			return err
-		}
-
-		var pol1, pol2 *models.CachedEdgePolicy
-		if dbPol1 != nil {
-			policy1, err := buildChanPolicy(
-				*dbPol1, edge.ChannelID, nil, node2, true,
-			)
-			if err != nil {
-				return err
-			}
-
-			pol1 = models.NewCachedPolicy(policy1)
-		}
-		if dbPol2 != nil {
-			policy2, err := buildChanPolicy(
-				*dbPol2, edge.ChannelID, nil, node1, false,
-			)
-			if err != nil {
-				return err
-			}
-
-			pol2 = models.NewCachedPolicy(policy2)
-		}
-
-		if err := cb(edge, pol1, pol2); err != nil {
-			return err
-		}
-
-		return nil
-	}
-
-	return s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
-		lastID := int64(-1)
-		for {
-			//nolint:ll
-			rows, err := db.ListChannelsWithPoliciesPaginated(
-				ctx, sqlc.ListChannelsWithPoliciesPaginatedParams{
-					Version: int16(ProtocolV1),
-					ID:      lastID,
-					Limit:   pageSize,
-				},
-			)
-			if err != nil {
-				return err
-			}
-
-			if len(rows) == 0 {
-				break
-			}
-
-			for _, row := range rows {
-				err := handleChannel(db, row)
-				if err != nil {
-					return err
-				}
-
-				lastID = row.Channel.ID
-			}
-		}
-
-		return nil
-	}, sqldb.NoOpReset)
-}
-
 // ForEachChannel iterates through all the channel edges stored within the
 // graph and invokes the passed callback for each edge. The callback takes two
 // edges as since this is a directed graph, both the in/out edges are visited.
@@ -1395,7 +1291,7 @@ func (s *SQLStore) ForEachChannel(cb func(*models.ChannelEdgeInfo,
 	}
 
 	return s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
-		lastID := int64(-1)
+		var lastID int64
 		for {
 			//nolint:ll
 			rows, err := db.ListChannelsWithPoliciesPaginated(
@@ -2679,155 +2575,6 @@ func (s *SQLStore) DisconnectBlockAtHeight(height uint32) (
 	return removedChans, nil
 }
 
-// AddEdgeProof sets the proof of an existing edge in the graph database.
-//
-// NOTE: part of the V1Store interface.
-func (s *SQLStore) AddEdgeProof(scid lnwire.ShortChannelID,
-	proof *models.ChannelAuthProof) error {
-
-	var (
-		ctx       = context.TODO()
-		scidBytes = channelIDToBytes(scid.ToUint64())
-	)
-
-	err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
-		res, err := db.AddV1ChannelProof(
-			ctx, sqlc.AddV1ChannelProofParams{
-				Scid:              scidBytes[:],
-				Node1Signature:    proof.NodeSig1Bytes,
-				Node2Signature:    proof.NodeSig2Bytes,
-				Bitcoin1Signature: proof.BitcoinSig1Bytes,
-				Bitcoin2Signature: proof.BitcoinSig2Bytes,
-			},
-		)
-		if err != nil {
-			return fmt.Errorf("unable to add edge proof: %w", err)
-		}
-
-		n, err := res.RowsAffected()
-		if err != nil {
-			return err
-		}
-
-		if n == 0 {
-			return fmt.Errorf("no rows affected when adding edge "+
-				"proof for SCID %v", scid)
-		} else if n > 1 {
-			return fmt.Errorf("multiple rows affected when adding "+
-				"edge proof for SCID %v: %d rows affected",
-				scid, n)
-		}
-
-		return nil
-	}, sqldb.NoOpReset)
-	if err != nil {
-		return fmt.Errorf("unable to add edge proof: %w", err)
-	}
-
-	return nil
-}
-
-// PutClosedScid stores a SCID for a closed channel in the database. This is so
-// that we can ignore channel announcements that we know to be closed without
-// having to validate them and fetch a block.
-//
-// NOTE: part of the V1Store interface.
-func (s *SQLStore) PutClosedScid(scid lnwire.ShortChannelID) error {
-	var (
-		ctx     = context.TODO()
-		chanIDB = channelIDToBytes(scid.ToUint64())
-	)
-
-	return s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
-		return db.InsertClosedChannel(ctx, chanIDB[:])
-	}, sqldb.NoOpReset)
-}
-
-// IsClosedScid checks whether a channel identified by the passed in scid is
-// closed. This helps avoid having to perform expensive validation checks.
-//
-// NOTE: part of the V1Store interface.
-func (s *SQLStore) IsClosedScid(scid lnwire.ShortChannelID) (bool, error) {
-	var (
-		ctx      = context.TODO()
-		isClosed bool
-		chanIDB  = channelIDToBytes(scid.ToUint64())
-	)
-	err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
-		var err error
-		isClosed, err = db.IsClosedChannel(ctx, chanIDB[:])
-		if err != nil {
-			return fmt.Errorf("unable to fetch closed channel: %w",
-				err)
-		}
-
-		return nil
-	}, sqldb.NoOpReset)
-	if err != nil {
-		return false, fmt.Errorf("unable to fetch closed channel: %w",
-			err)
-	}
-
-	return isClosed, nil
-}
-
-// GraphSession will provide the call-back with access to a NodeTraverser
-// instance which can be used to perform queries against the channel graph.
-//
-// NOTE: part of the V1Store interface.
-func (s *SQLStore) GraphSession(cb func(graph NodeTraverser) error) error {
-	var ctx = context.TODO()
-
-	return s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
-		return cb(newSQLNodeTraverser(db, s.cfg.ChainHash))
-	}, sqldb.NoOpReset)
-}
-
-// sqlNodeTraverser implements the NodeTraverser interface but with a backing
-// read only transaction for a consistent view of the graph.
-type sqlNodeTraverser struct {
-	db    SQLQueries
-	chain chainhash.Hash
-}
-
-// A compile-time assertion to ensure that sqlNodeTraverser implements the
-// NodeTraverser interface.
-var _ NodeTraverser = (*sqlNodeTraverser)(nil)
-
-// newSQLNodeTraverser creates a new instance of the sqlNodeTraverser.
-func newSQLNodeTraverser(db SQLQueries,
-	chain chainhash.Hash) *sqlNodeTraverser {
-
-	return &sqlNodeTraverser{
-		db:    db,
-		chain: chain,
-	}
-}
-
-// ForEachNodeDirectedChannel calls the callback for every channel of the given
-// node.
-//
-// NOTE: Part of the NodeTraverser interface.
-func (s *sqlNodeTraverser) ForEachNodeDirectedChannel(nodePub route.Vertex,
-	cb func(channel *DirectedChannel) error) error {
-
-	ctx := context.TODO()
-
-	return forEachNodeDirectedChannel(ctx, s.db, nodePub, cb)
-}
-
-// FetchNodeFeatures returns the features of the given node. If the node is
-// unknown, assume no additional features are supported.
-//
-// NOTE: Part of the NodeTraverser interface.
-func (s *sqlNodeTraverser) FetchNodeFeatures(nodePub route.Vertex) (
-	*lnwire.FeatureVector, error) {
-
-	ctx := context.TODO()
-
-	return fetchNodeFeatures(ctx, s.db, nodePub)
-}
-
 // 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 node is not found, no error is
@@ -2957,7 +2704,7 @@ func forEachNodeDirectedChannel(ctx context.Context, db SQLQueries,
 func forEachNodeCacheable(ctx context.Context, db SQLQueries,
 	cb func(nodeID int64, nodePub route.Vertex) error) error {
 
-	lastID := int64(-1)
+	var lastID int64
 
 	for {
 		nodes, err := db.ListNodeIDsAndPubKeys(
diff --git a/routing/pathfind.go b/routing/pathfind.go
index f489173f9..ace174fef 100644
--- a/routing/pathfind.go
+++ b/routing/pathfind.go
@@ -1227,46 +1227,24 @@ func findBlindedPaths(g Graph, target route.Vertex,
 	}
 
 	var (
-		// The target node is always the last hop in the path, and so
-		// we add it to the incoming path from the get-go. Any additions
-		// to the slice should be prepended.
-		incomingPath = []blindedHop{{
-			vertex: target,
-		}}
-
-		// supportsRouteBlinding is a list of nodes that we can assume
-		// support route blinding without needing to rely on the feature
-		// bits announced in their node announcement. Since we are
-		// finding a path to the target node, we can assume it supports
-		// route blinding.
-		supportsRouteBlinding = map[route.Vertex]bool{
-			target: true,
-		}
-
+		// The target node is always the last hop in the path.
+		incomingPath     = []blindedHop{{vertex: target}}
+		whiteListedNodes = map[route.Vertex]bool{target: true}
 		visited          = make(map[route.Vertex]bool)
+		errChanFound     = errors.New("found incoming channel")
 		nextTarget       = target
-		haveIncomingPath = len(restrictions.incomingChainedChannels) > 0
-
-		// errChanFound is an error variable we return from the DB
-		// iteration call below when we have found the channel we are
-		// looking for. This lets us exit the iteration early.
-		errChanFound = errors.New("found incoming channel")
 	)
 	for _, chanID := range restrictions.incomingChainedChannels {
-		// Mark that we have visited this node so that we don't revisit
-		// it later on when we call "processNodeForBlindedPath".
 		visited[nextTarget] = true
 
 		err := g.ForEachNodeDirectedChannel(nextTarget,
 			func(channel *graphdb.DirectedChannel) error {
-				// This is not the right channel, continue to
-				// the node's other channels.
+				// Not the right channel, continue to the node's
+				// other channels.
 				if channel.ChannelID != chanID {
 					return nil
 				}
 
-				// We found the channel in question. Prepend it
-				// to the incoming path.
 				incomingPath = append([]blindedHop{
 					{
 						vertex:       channel.OtherNode,
@@ -1281,37 +1259,39 @@ func findBlindedPaths(g Graph, target route.Vertex,
 				return errChanFound
 			},
 		)
-		// We expect errChanFound to be returned if the channel in
-		// question was found.
-		if !errors.Is(err, errChanFound) && err != nil {
-			return nil, err
-		} else if err == nil {
+		if err == nil {
 			return nil, fmt.Errorf("incoming channel %d is not "+
 				"seen as owned by node %v", chanID, nextTarget)
 		}
+		if !errors.Is(err, errChanFound) {
+			return nil, err
+		}
 
 		// Check that the user didn't accidentally add a channel that
-		// is owned by a node in the node omission set.
+		// is owned by a node in the node omission set
 		if restrictions.nodeOmissionSet.Contains(nextTarget) {
 			return nil, fmt.Errorf("node %v cannot simultaneously "+
 				"be included in the omission set and in the "+
 				"partially specified path", nextTarget)
 		}
 
-		// Check that we have not already visited the next target node
-		// since this would mean a circular incoming path.
-		if visited[nextTarget] {
+		if whiteListedNodes[nextTarget] {
 			return nil, fmt.Errorf("a circular route cannot be " +
 				"specified for the incoming blinded path")
 		}
-
-		supportsRouteBlinding[nextTarget] = true
+		whiteListedNodes[nextTarget] = true
 	}
 
-	// A helper closure which checks if the node in question has advertised
-	// that it supports route blinding.
-	nodeSupportsRouteBlinding := func(node route.Vertex) (bool, error) {
-		if supportsRouteBlinding[node] {
+	// If the node is not the destination node, then it is required that the
+	// node advertise the route blinding feature-bit in order for it to be
+	// chosen as a node on the blinded path.
+	// We skip checking the target node, as accepting blinded payments
+	// (via invoice) doesn't imply support for routing them (via node
+	// announcement).
+	// We skip checking incomingChainedChannels nodes, as we might not yet
+	// have an updated node announcement for them.
+	supportsRouteBlinding := func(node route.Vertex) (bool, error) {
+		if whiteListedNodes[node] {
 			return true, nil
 		}
 
@@ -1328,46 +1308,39 @@ func findBlindedPaths(g Graph, target route.Vertex,
 	// conditions such as: The maxHops number being reached or reaching
 	// a node that doesn't have any other edges - in that final case, the
 	// whole path should be ignored.
-	//
-	// NOTE: any paths returned will end at the "nextTarget" node meaning
-	// that if we have a fixed list of incoming chained channels, then this
-	// fixed list must be appended to any of the returned paths.
 	paths, _, err := processNodeForBlindedPath(
-		g, nextTarget, nodeSupportsRouteBlinding, visited, restrictions,
+		g, nextTarget, supportsRouteBlinding, visited, restrictions,
 	)
 	if err != nil {
 		return nil, err
 	}
 
-	// Reverse each path so that the order is correct (from introduction
-	// node to last hop node) and then append the incoming path (if any was
-	// specified) to the end of the path.
-	orderedPaths := make([][]blindedHop, 0, len(paths))
-	for _, path := range paths {
-		sort.Slice(path, func(i, j int) bool {
-			return j < i
-		})
-
-		orderedPaths = append(
-			orderedPaths, append(path, incomingPath...),
-		)
-	}
-
-	// There is a chance we have an incoming path that by itself satisfies
-	// the minimum hop restriction. In that case, we add it as its own path.
-	if haveIncomingPath &&
-		len(incomingPath) > int(restrictions.minNumHops) {
+	orderedPaths := make([][]blindedHop, len(paths))
+
+	// When there is no path to add, but incomingChainedChannels can be
+	// used.
+	lenChainedChannels := int8(len(restrictions.incomingChainedChannels))
+	if len(paths) == 0 && lenChainedChannels > 0 {
+		orderedPaths = [][]blindedHop{incomingPath}
+	} else {
+		// Reverse each path so that the order is correct (from
+		// introduction node to last hop node) and then append this node
+		// on as the destination of each path.
+		for i, path := range paths {
+			sort.Slice(path, func(i, j int) bool {
+				return j < i
+			})
 
-		orderedPaths = append(orderedPaths, incomingPath)
+			orderedPaths[i] = append(path, incomingPath...)
+		}
 	}
 
 	// Handle the special case that allows a blinded path with the
-	// introduction node as the destination node. This only applies if no
-	// incoming path was specified since in that case, by definition, the
-	// caller wants a non-zero length blinded path.
-	if restrictions.minNumHops == 0 && !haveIncomingPath {
+	// introduction node as the destination node.
+	if restrictions.minNumHops == 0 && lenChainedChannels == 0 {
 		singleHopPath := [][]blindedHop{{{vertex: target}}}
 
+		//nolint:makezero
 		orderedPaths = append(
 			orderedPaths, singleHopPath...,
 		)
diff --git a/routing/router.go b/routing/router.go
index b0c6bd323..0ce9a1ca8 100644
--- a/routing/router.go
+++ b/routing/router.go
@@ -699,8 +699,8 @@ func (r *ChannelRouter) FindBlindedPaths(destination route.Vertex,
 				"path since it resulted in an low "+
 				"probability path(%.3f)",
 				route.ChanIDString(routeWithProbability.route),
-				routeWithProbability.probability)
-
+				routeWithProbability.probability,
+			)
 			continue
 		}
 
diff --git a/sqldb/sqlc/graph.sql.go b/sqldb/sqlc/graph.sql.go
index 0387f612e..c74b4c050 100644
--- a/sqldb/sqlc/graph.sql.go
+++ b/sqldb/sqlc/graph.sql.go
@@ -26,34 +26,6 @@ func (q *Queries) AddSourceNode(ctx context.Context, nodeID int64) error {
 	return err
 }
 
-const addV1ChannelProof = `-- name: AddV1ChannelProof :execresult
-UPDATE channels
-SET node_1_signature = $2,
-    node_2_signature = $3,
-    bitcoin_1_signature = $4,
-    bitcoin_2_signature = $5
-WHERE scid = $1
-  AND version = 1
-`
-
-type AddV1ChannelProofParams struct {
-	Scid              []byte
-	Node1Signature    []byte
-	Node2Signature    []byte
-	Bitcoin1Signature []byte
-	Bitcoin2Signature []byte
-}
-
-func (q *Queries) AddV1ChannelProof(ctx context.Context, arg AddV1ChannelProofParams) (sql.Result, error) {
-	return q.db.ExecContext(ctx, addV1ChannelProof,
-		arg.Scid,
-		arg.Node1Signature,
-		arg.Node2Signature,
-		arg.Bitcoin1Signature,
-		arg.Bitcoin2Signature,
-	)
-}
-
 const countZombieChannels = `-- name: CountZombieChannels :one
 SELECT COUNT(*)
 FROM zombie_channels
@@ -1555,22 +1527,6 @@ func (q *Queries) InsertChannelFeature(ctx context.Context, arg InsertChannelFea
 	return err
 }
 
-const insertClosedChannel = `-- name: InsertClosedChannel :exec
-/* ─────────────────────────────────────────────
-   closed_scid table queries
-   ────────────────────────────────────────────-
-*/
-
-INSERT INTO closed_scids (scid)
-VALUES ($1)
-ON CONFLICT (scid) DO NOTHING
-`
-
-func (q *Queries) InsertClosedChannel(ctx context.Context, scid []byte) error {
-	_, err := q.db.ExecContext(ctx, insertClosedChannel, scid)
-	return err
-}
-
 const insertNodeAddress = `-- name: InsertNodeAddress :exec
 /* ─────────────────────────────────────────────
    node_addresses table queries
@@ -1627,21 +1583,6 @@ func (q *Queries) InsertNodeFeature(ctx context.Context, arg InsertNodeFeaturePa
 	return err
 }
 
-const isClosedChannel = `-- name: IsClosedChannel :one
-SELECT EXISTS (
-    SELECT 1
-    FROM closed_scids
-    WHERE scid = $1
-)
-`
-
-func (q *Queries) IsClosedChannel(ctx context.Context, scid []byte) (bool, error) {
-	row := q.db.QueryRowContext(ctx, isClosedChannel, scid)
-	var exists bool
-	err := row.Scan(&exists)
-	return exists, err
-}
-
 const isPublicV1Node = `-- name: IsPublicV1Node :one
 SELECT EXISTS (
     SELECT 1
diff --git a/sqldb/sqlc/migrations/000007_graph.down.sql b/sqldb/sqlc/migrations/000007_graph.down.sql
index db90d8976..396471196 100644
--- a/sqldb/sqlc/migrations/000007_graph.down.sql
+++ b/sqldb/sqlc/migrations/000007_graph.down.sql
@@ -29,4 +29,3 @@ DROP TABLE IF EXISTS nodes;
 DROP TABLE IF EXISTS channel_policy_extra_types;
 DROP TABLE IF EXISTS zombie_channels;
 DROP TABLE IF EXISTS prune_log;
-DROP TABLE IF EXISTS closed_scids;
diff --git a/sqldb/sqlc/migrations/000007_graph.up.sql b/sqldb/sqlc/migrations/000007_graph.up.sql
index c56478ec5..80a58cbd1 100644
--- a/sqldb/sqlc/migrations/000007_graph.up.sql
+++ b/sqldb/sqlc/migrations/000007_graph.up.sql
@@ -331,9 +331,4 @@ CREATE TABLE IF NOT EXISTS prune_log (
 
     -- The block hash that the prune was performed at.
     block_hash BLOB NOT NULL
-);
-
-CREATE TABLE IF NOT EXISTS closed_scids (
-    -- The short channel id of the channel.
-    scid BLOB PRIMARY KEY
 );
\ No newline at end of file
diff --git a/sqldb/sqlc/models.go b/sqldb/sqlc/models.go
index 887870b1c..2bb61f769 100644
--- a/sqldb/sqlc/models.go
+++ b/sqldb/sqlc/models.go
@@ -78,10 +78,6 @@ type ChannelPolicyExtraType struct {
 	Value           []byte
 }
 
-type ClosedScid struct {
-	Scid []byte
-}
-
 type Invoice struct {
 	ID                 int64
 	Hash               []byte
diff --git a/sqldb/sqlc/querier.go b/sqldb/sqlc/querier.go
index 28df66d90..f0e1ad3e7 100644
--- a/sqldb/sqlc/querier.go
+++ b/sqldb/sqlc/querier.go
@@ -12,7 +12,6 @@ import (
 
 type Querier interface {
 	AddSourceNode(ctx context.Context, nodeID int64) error
-	AddV1ChannelProof(ctx context.Context, arg AddV1ChannelProofParams) (sql.Result, error)
 	ClearKVInvoiceHashIndex(ctx context.Context) error
 	CountZombieChannels(ctx context.Context, version int16) (int64, error)
 	CreateChannel(ctx context.Context, arg CreateChannelParams) (int64, error)
@@ -80,7 +79,6 @@ type Querier interface {
 	InsertAMPSubInvoiceHTLC(ctx context.Context, arg InsertAMPSubInvoiceHTLCParams) error
 	InsertChanPolicyExtraType(ctx context.Context, arg InsertChanPolicyExtraTypeParams) error
 	InsertChannelFeature(ctx context.Context, arg InsertChannelFeatureParams) error
-	InsertClosedChannel(ctx context.Context, scid []byte) error
 	InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int64, error)
 	InsertInvoiceFeature(ctx context.Context, arg InsertInvoiceFeatureParams) error
 	InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) (int64, error)
@@ -89,7 +87,6 @@ type Querier interface {
 	InsertMigratedInvoice(ctx context.Context, arg InsertMigratedInvoiceParams) (int64, error)
 	InsertNodeAddress(ctx context.Context, arg InsertNodeAddressParams) error
 	InsertNodeFeature(ctx context.Context, arg InsertNodeFeatureParams) error
-	IsClosedChannel(ctx context.Context, scid []byte) (bool, error)
 	IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error)
 	IsZombieChannel(ctx context.Context, arg IsZombieChannelParams) (bool, error)
 	ListChannelsByNodeID(ctx context.Context, arg ListChannelsByNodeIDParams) ([]ListChannelsByNodeIDRow, error)
diff --git a/sqldb/sqlc/queries/graph.sql b/sqldb/sqlc/queries/graph.sql
index 681f80def..67b5dae6b 100644
--- a/sqldb/sqlc/queries/graph.sql
+++ b/sqldb/sqlc/queries/graph.sql
@@ -208,15 +208,6 @@ INSERT INTO channels (
 )
 RETURNING id;
 
--- name: AddV1ChannelProof :execresult
-UPDATE channels
-SET node_1_signature = $2,
-    node_2_signature = $3,
-    bitcoin_1_signature = $4,
-    bitcoin_2_signature = $5
-WHERE scid = $1
-  AND version = 1;
-
 -- name: GetChannelsBySCIDRange :many
 SELECT sqlc.embed(c),
     n1.pub_key AS node1_pub_key,
@@ -709,20 +700,3 @@ LIMIT 1;
 DELETE FROM prune_log
 WHERE block_height >= @start_height
   AND block_height <= @end_height;
-
-/* ─────────────────────────────────────────────
-   closed_scid table queries
-   ────────────────────────────────────────────-
-*/
-
--- name: InsertClosedChannel :exec
-INSERT INTO closed_scids (scid)
-VALUES ($1)
-ON CONFLICT (scid) DO NOTHING;
-
--- name: IsClosedChannel :one
-SELECT EXISTS (
-    SELECT 1
-    FROM closed_scids
-    WHERE scid = $1
-);


Test patch:
diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go
index 97da79dbd..419c96bc3 100644
--- a/graph/db/graph_test.go
+++ b/graph/db/graph_test.go
@@ -1128,7 +1128,7 @@ func TestAddEdgeProof(t *testing.T) {
 	t.Parallel()
 	ctx := context.Background()
 
-	graph := MakeTestGraphNew(t)
+	graph := MakeTestGraph(t)
 
 	// Add an edge with no proof.
 	node1 := createTestVertex(t)
@@ -4325,7 +4325,7 @@ func TestGraphLoading(t *testing.T) {
 func TestClosedScid(t *testing.T) {
 	t.Parallel()
 
-	graph := MakeTestGraphNew(t)
+	graph := MakeTestGraph(t)
 
 	scid := lnwire.ShortChannelID{}
 
diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go
index 0e3c61f61..2aadb5755 100644
--- a/itest/lnd_route_blinding_test.go
+++ b/itest/lnd_route_blinding_test.go
@@ -1428,55 +1428,25 @@ func testBlindedPaymentHTLCReForward(ht *lntest.HarnessTest) {
 //     pre-specified.
 func testPartiallySpecifiedBlindedPath(ht *lntest.HarnessTest) {
 	// Create a six hop network:
-	// 	Alice -> Bob -> Carol -> Dave -> Eve -> Frank.
-	// Carol will be the node generating invoices containing blinded paths.
+	// Alice -> Bob -> Carol -> Dave -> Eve -> Frank.
+	chanAmt := btcutil.Amount(100000)
+	cfgs := [][]string{nil, nil, nil, nil, nil, nil}
 	chanPoints, nodes := ht.CreateSimpleNetwork(
-		[][]string{nil, nil, nil, nil, nil, nil},
-		lntest.OpenChannelParams{
-			Amt:     chanAmt,
-			PushAmt: chanAmt / 2,
-		},
+		cfgs, lntest.OpenChannelParams{Amt: chanAmt},
 	)
 
-	var (
-		alice = nodes[0]
-		bob   = nodes[1]
-		carol = nodes[2]
-		dave  = nodes[3]
-		eve   = nodes[4]
-		frank = nodes[5]
-	)
+	alice, bob, carol, dave, eve := nodes[0], nodes[1], nodes[2], nodes[3],
+		nodes[4]
+
+	chanPointAliceBob, chanPointBobCarol, chanPointCarolDave :=
+		chanPoints[0], chanPoints[1], chanPoints[2]
 
 	// Lookup full channel info so that we have channel ids for our route.
-	aliceBobChan := ht.GetChannelByChanPoint(alice, chanPoints[0])
-	bobCarolChan := ht.GetChannelByChanPoint(bob, chanPoints[1])
-	carolDaveChan := ht.GetChannelByChanPoint(carol, chanPoints[2])
-
-	// assertPathDetails is a helper function that asserts the details of
-	// the blinded paths returned in the invoice.
-	assertPathDetails := func(invoice *lnrpc.AddInvoiceResponse,
-		expPathLengths int, expIntroNodes ...[]byte) {
-
-		payReq := carol.RPC.DecodePayReq(invoice.PaymentRequest)
-		paths := payReq.BlindedPaths
-
-		introNodes := make([][]byte, 0, len(paths))
-		for _, p := range paths {
-			require.Len(
-				ht, p.BlindedPath.BlindedHops, expPathLengths,
-			)
-
-			introNodes = append(
-				introNodes, p.BlindedPath.IntroductionNode,
-			)
-		}
-		require.ElementsMatch(ht, introNodes, expIntroNodes)
-	}
+	aliceBobChan := ht.GetChannelByChanPoint(alice, chanPointAliceBob)
+	bobCarolChan := ht.GetChannelByChanPoint(bob, chanPointBobCarol)
+	carolDaveChan := ht.GetChannelByChanPoint(carol, chanPointCarolDave)
 
-	// Let carol set no incoming channel restrictions. With a min of 1 hop,
-	// we expect the following blinded paths to be included:
-	//  1) Bob->Carol
-	//  2) Dave->Carol
+	// Let carol set no incoming channel restrictions.
 	var (
 		minNumRealHops uint32 = 1
 		numHops        uint32 = 1
@@ -1490,94 +1460,122 @@ func testPartiallySpecifiedBlindedPath(ht *lntest.HarnessTest) {
 			NumHops:        &numHops,
 		},
 	})
-	assertPathDetails(invoice, 2, bob.PubKey[:], dave.PubKey[:])
 
-	// Let carol choose an invalid incoming channel list for the blinded
-	// path. It is invalid since it does not connect to her node.
-	// Assert that the expected error is returned.
+	introNodesFound := make([][]byte, 0)
+	introNodesExpected := [][]byte{bob.PubKey[:], dave.PubKey[:]}
+
+	// Assert that it contains two blinded path with only 2 hops each one.
+	payReq := carol.RPC.DecodePayReq(invoice.PaymentRequest)
+	require.Len(ht, payReq.BlindedPaths, 2)
+	path := payReq.BlindedPaths[0].BlindedPath
+	require.Len(ht, path.BlindedHops, 2)
+	introNodesFound = append(introNodesFound, path.IntroductionNode)
+	path = payReq.BlindedPaths[1].BlindedPath
+	require.Len(ht, path.BlindedHops, 2)
+	introNodesFound = append(introNodesFound, path.IntroductionNode)
+
+	// Assert the introduction nodes without caring about the routes order.
+	require.ElementsMatch(ht, introNodesExpected, introNodesFound)
+
+	// Let carol choose the wrong incoming channel.
+	var (
+		incomingChannelList = []uint64{aliceBobChan.ChanId}
+	)
+
+	err := fmt.Sprintf("incoming channel %v is not seen as owned by node",
+		aliceBobChan.ChanId)
+
 	carol.RPC.AddInvoiceAssertErr(
 		&lnrpc.Invoice{
 			Memo:      "test",
 			ValueMsat: 10_000_000,
 			IsBlinded: true,
 			BlindedPathConfig: &lnrpc.BlindedPathConfig{
-				MinNumRealHops: &minNumRealHops,
-				NumHops:        &numHops,
-				IncomingChannelList: []uint64{
-					aliceBobChan.ChanId,
-				},
+				MinNumRealHops:      &minNumRealHops,
+				NumHops:             &numHops,
+				IncomingChannelList: incomingChannelList,
 			},
 		},
-		fmt.Sprintf("incoming channel %v is not seen as owned by node",
-			aliceBobChan.ChanId),
+		err,
 	)
 
 	// Let Carol set the incoming channel list greater than minimum number
 	// of real hops.
-	// Assert that the expected error is returned.
+	incomingChannelList = []uint64{aliceBobChan.ChanId, bobCarolChan.ChanId}
+	err = fmt.Sprintf("minimum number of blinded path hops (%d) must be "+
+		"greater than or equal to the number of hops specified on "+
+		"the chained channels (%d)", minNumRealHops,
+		len(incomingChannelList),
+	)
+
 	carol.RPC.AddInvoiceAssertErr(
 		&lnrpc.Invoice{
 			Memo:      "test",
 			ValueMsat: 10_000_000,
 			IsBlinded: true,
 			BlindedPathConfig: &lnrpc.BlindedPathConfig{
-				MinNumRealHops: &minNumRealHops,
-				NumHops:        &numHops,
-				IncomingChannelList: []uint64{
-					aliceBobChan.ChanId,
-					bobCarolChan.ChanId,
-				},
+				MinNumRealHops:      &minNumRealHops,
+				NumHops:             &numHops,
+				IncomingChannelList: incomingChannelList,
 			},
 		},
-		"must be greater than or equal to the number of hops "+
-			"specified on the chained channels",
+		err,
 	)
 
 	// Let Carol choose an incoming channel that points to a node in the
 	// omission set.
+	incomingChannelList = []uint64{bobCarolChan.ChanId}
+	var nodeOmissionList [][]byte
+	nodeOmissionList = append(nodeOmissionList, bob.PubKey[:])
+
+	err = fmt.Sprintf("cannot simultaneously be included in the omission " +
+		"set and in the partially specified path",
+	)
+
 	carol.RPC.AddInvoiceAssertErr(
 		&lnrpc.Invoice{
 			Memo:      "test",
 			ValueMsat: 10_000_000,
 			IsBlinded: true,
 			BlindedPathConfig: &lnrpc.BlindedPathConfig{
-				MinNumRealHops: &minNumRealHops,
-				NumHops:        &numHops,
-				IncomingChannelList: []uint64{
-					bobCarolChan.ChanId,
-				},
-				NodeOmissionList: [][]byte{
-					bob.PubKey[:],
-				},
+				MinNumRealHops:      &minNumRealHops,
+				NumHops:             &numHops,
+				IncomingChannelList: incomingChannelList,
+				NodeOmissionList:    nodeOmissionList,
 			},
 		},
-		"cannot simultaneously be included in the omission set and "+
-			"in the partially specified path",
+		err,
 	)
 
 	// Let carol restrict bob as incoming channel.
+	incomingChannelList = []uint64{bobCarolChan.ChanId}
+
 	invoice = carol.RPC.AddInvoice(&lnrpc.Invoice{
 		Memo:      "test",
 		ValueMsat: 10_000_000,
 		IsBlinded: true,
 		BlindedPathConfig: &lnrpc.BlindedPathConfig{
-			MinNumRealHops: &minNumRealHops,
-			NumHops:        &numHops,
-			IncomingChannelList: []uint64{
-				bobCarolChan.ChanId,
-			},
+			MinNumRealHops:      &minNumRealHops,
+			NumHops:             &numHops,
+			IncomingChannelList: incomingChannelList,
 		},
 	})
 
 	// Assert that it contains a single blinded path with only
 	// 2 hops, with bob as the introduction node.
-	assertPathDetails(invoice, 2, bob.PubKey[:])
+	payReq = carol.RPC.DecodePayReq(invoice.PaymentRequest)
+	require.Len(ht, payReq.BlindedPaths, 1)
+	path = payReq.BlindedPaths[0].BlindedPath
+	require.Len(ht, path.BlindedHops, 2)
+	require.EqualValues(ht, path.IntroductionNode, bob.PubKey[:])
 
-	// Check that Alice can pay the invoice.
+	// Now let alice pay the invoice.
 	ht.CompletePaymentRequests(alice, []string{invoice.PaymentRequest})
 
-	// Let carol restrict dave as incoming channel and max hops as 2.
+	// Let carol restrict dave as incoming channel and max Hops as 2
 	numHops = 2
+	incomingChannelList = []uint64{carolDaveChan.ChanId}
+
 	invoice = carol.RPC.AddInvoice(&lnrpc.Invoice{
 		Memo:      "test",
 		ValueMsat: 10_000_000,
@@ -1585,15 +1583,16 @@ func testPartiallySpecifiedBlindedPath(ht *lntest.HarnessTest) {
 		BlindedPathConfig: &lnrpc.BlindedPathConfig{
 			MinNumRealHops:      &minNumRealHops,
 			NumHops:             &numHops,
-			IncomingChannelList: []uint64{carolDaveChan.ChanId},
+			IncomingChannelList: incomingChannelList,
 		},
 	})
 
-	// Assert that it contains two paths: one 3 hop one starting at Eve and
-	// a 3 hop one starting at Dave (this one will be padded with a dummy
-	// hop) in order to keep all the paths equidistant.
-	assertPathDetails(invoice, 3, eve.PubKey[:], dave.PubKey[:])
-
-	// Check that Frank can pay the invoice.
-	ht.CompletePaymentRequests(frank, []string{invoice.PaymentRequest})
+	// Assert that it contains one path with 3 hops, with dave as the
+	// introduction node. The path alice -> bob -> carol is discarded
+	// because alice is a dead-end.
+	payReq = carol.RPC.DecodePayReq(invoice.PaymentRequest)
+	require.Len(ht, payReq.BlindedPaths, 1)
+	path = payReq.BlindedPaths[0].BlindedPath
+	require.Len(ht, path.BlindedHops, 3)
+	require.EqualValues(ht, path.IntroductionNode, eve.PubKey[:])
 }
diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go
index 45faedaf1..e1962f5d9 100644
--- a/routing/pathfind_test.go
+++ b/routing/pathfind_test.go
@@ -160,9 +160,7 @@ func makeTestGraph(t *testing.T, useCache bool) (*graphdb.ChannelGraph,
 	kvdb.Backend, error) {
 
 	// Create channelgraph for the first time.
-	graph := graphdb.MakeTestGraphNew(
-		t, graphdb.WithUseGraphCache(useCache),
-	)
+	graph := graphdb.MakeTestGraph(t, graphdb.WithUseGraphCache(useCache))
 	require.NoError(t, graph.Start())
 	t.Cleanup(func() {
 		require.NoError(t, graph.Stop())
@@ -291,11 +289,6 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
 			}
 
 			source = dbNode
-
-			// If this is the source node, we don't have to call
-			// AddLightningNode below since we will call
-			// SetSourceNode later.
-			continue
 		}
 
 		// With the node fully parsed, add it as a vertex within the
@@ -545,11 +538,10 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 
 	aliasMap := make(map[string]route.Vertex)
 	privKeyMap := make(map[string]*btcec.PrivateKey)
-	channelIDs := make(map[route.Vertex]map[route.Vertex]uint64)
 
 	nodeIndex := byte(0)
-	addNodeWithAlias := func(alias string,
-		features *lnwire.FeatureVector) error {
+	addNodeWithAlias := func(alias string, features *lnwire.FeatureVector) (
+		*models.LightningNode, error) {
 
 		keyBytes := []byte{
 			0, 0, 0, 0, 0, 0, 0, 0,
@@ -579,22 +571,18 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 
 		// With the node fully parsed, add it as a vertex within the
 		// graph.
-		if alias == source {
-			err = graph.SetSourceNode(ctx, dbNode)
-			require.NoError(t, err)
-		} else {
-			err := graph.AddLightningNode(ctx, dbNode)
-			require.NoError(t, err)
+		if err := graph.AddLightningNode(ctx, dbNode); err != nil {
+			return nil, err
 		}
 
 		aliasMap[alias] = dbNode.PubKeyBytes
 		nodeIndex++
 
-		return nil
+		return dbNode, nil
 	}
 
 	// Add the source node.
-	err = addNodeWithAlias(
+	dbNode, err := addNodeWithAlias(
 		source, lnwire.NewFeatureVector(
 			lnwire.NewRawFeatureVector(sourceFeatureBits...),
 			lnwire.Features,
@@ -602,6 +590,10 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 	)
 	require.NoError(t, err)
 
+	if err = graph.SetSourceNode(ctx, dbNode); err != nil {
+		return nil, err
+	}
+
 	// Initialize variable that keeps track of the next channel id to assign
 	// if none is specified.
 	nextUnassignedChannelID := uint64(100000)
@@ -619,7 +611,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 					features =
 						node.testChannelPolicy.Features
 				}
-				err := addNodeWithAlias(
+				_, err := addNodeWithAlias(
 					node.Alias, features,
 				)
 				if err != nil {
@@ -660,16 +652,6 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 			node1Vertex, node2Vertex = node2Vertex, node1Vertex
 		}
 
-		if _, ok := channelIDs[node1Vertex]; !ok {
-			channelIDs[node1Vertex] = map[route.Vertex]uint64{}
-		}
-		channelIDs[node1Vertex][node2Vertex] = channelID
-
-		if _, ok := channelIDs[node2Vertex]; !ok {
-			channelIDs[node2Vertex] = map[route.Vertex]uint64{}
-		}
-		channelIDs[node2Vertex][node1Vertex] = channelID
-
 		// We first insert the existence of the edge between the two
 		// nodes.
 		edgeInfo := models.ChannelEdgeInfo{
@@ -783,7 +765,6 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
 		mcBackend:  graphBackend,
 		aliasMap:   aliasMap,
 		privKeyMap: privKeyMap,
-		channelIDs: channelIDs,
 		links:      links,
 	}, nil
 }
@@ -2176,7 +2157,6 @@ func runRouteFailMaxHTLC(t *testing.T, useCache bool) {
 	require.NoError(t, err, "unable to fetch channel edges by ID")
 	midEdge.MessageFlags = 1
 	midEdge.MaxHTLC = payAmt - 1
-	midEdge.LastUpdate = midEdge.LastUpdate.Add(time.Second)
 	err = graph.UpdateEdgePolicy(context.Background(), midEdge)
 	require.NoError(t, err)
 
@@ -2219,12 +2199,10 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
 	_, e1, e2, err := graph.graph.FetchChannelEdgesByID(roasToPham)
 	require.NoError(t, err, "unable to fetch edge")
 	e1.ChannelFlags |= lnwire.ChanUpdateDisabled
-	e1.LastUpdate = e1.LastUpdate.Add(time.Second)
 	if err := graph.graph.UpdateEdgePolicy(ctx, e1); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
 	}
 	e2.ChannelFlags |= lnwire.ChanUpdateDisabled
-	e2.LastUpdate = e2.LastUpdate.Add(time.Second)
 	if err := graph.graph.UpdateEdgePolicy(ctx, e2); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
 	}
@@ -2242,7 +2220,6 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
 	_, e, _, err := graph.graph.FetchChannelEdgesByID(phamToSophon)
 	require.NoError(t, err, "unable to fetch edge")
 	e.ChannelFlags |= lnwire.ChanUpdateDisabled
-	e.LastUpdate = e.LastUpdate.Add(time.Second)
 	if err := graph.graph.UpdateEdgePolicy(ctx, e); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
 	}
@@ -2325,12 +2302,10 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
 	_, e1, e2, err := graph.graph.FetchChannelEdgesByID(roasToSongoku)
 	require.NoError(t, err, "unable to fetch edge")
 	e1.ChannelFlags |= lnwire.ChanUpdateDisabled
-	e1.LastUpdate = e1.LastUpdate.Add(time.Second)
 	if err := graph.graph.UpdateEdgePolicy(ctx, e1); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
 	}
 	e2.ChannelFlags |= lnwire.ChanUpdateDisabled
-	e2.LastUpdate = e2.LastUpdate.Add(time.Second)
 	if err := graph.graph.UpdateEdgePolicy(ctx, e2); err != nil {
 		t.Fatalf("unable to update edge: %v", err)
 	}
@@ -3217,16 +3192,6 @@ func newPathFindingTestContext(t *testing.T, useCache bool,
 	return ctx
 }
 
-func (c *pathFindingTestContext) nodePairChannel(alias1, alias2 string) uint64 {
-	node1 := c.keyFromAlias(alias1)
-	node2 := c.keyFromAlias(alias2)
-
-	channel, ok := c.testGraphInstance.channelIDs[node1][node2]
-	require.True(c.t, ok)
-
-	return channel
-}
-
 func (c *pathFindingTestContext) keyFromAlias(alias string) route.Vertex {
 	return c.testGraphInstance.aliasMap[alias]
 }
@@ -3911,7 +3876,7 @@ func TestFindBlindedPaths(t *testing.T) {
 		"eve,bob,dave",
 	})
 
-	// 5) We will also test the special case where the destination node
+	// 5) Finally, we will test the special case where the destination node
 	// is also the recipient.
 	paths, err = ctx.findBlindedPaths(&blindedPathRestrictions{
 		minNumHops: 0,
@@ -3923,138 +3888,93 @@ func TestFindBlindedPaths(t *testing.T) {
 		"dave",
 	})
 
-	// 6) Now, we will test some cases where the user manually specifies
-	// the first few incoming channels of a route.
-	//
-	// 6.1) Let the user specify the B-D channel as the last hop with a
-	// max of 1 hop.
+	// 6) Restrict the min & max path length such that we only include paths
+	// with one being only the intro-node and the others with one hop other
+	// than the destination hop.
 	paths, err = ctx.findBlindedPaths(&blindedPathRestrictions{
-		minNumHops: 1,
+		minNumHops: 0,
 		maxNumHops: 1,
-		incomingChainedChannels: []uint64{
-			ctx.nodePairChannel("bob", "dave"),
-		},
 	})
 	require.NoError(t, err)
 
-	// If the max number of hops is 1, then only the B->D path is chosen
+	// We expect the following paths:
+	//	- D
+	//	- B, D
+	// 	- C, D
+	// The (A, D) path is not chosen since A does not advertise the route
+	// blinding feature bit. The (G, D) path is not chosen since G does not
+	// have any other known channels.
 	assertPaths(paths, []string{
+		"dave",
 		"bob,dave",
+		"charlie,dave",
 	})
 
-	// 6.2) Extend the search to include 2 hops along with the B-D channel
-	// restriction.
+	// 7) Restrict the min & max path length such that we only include paths
+	// with one hop other than the destination hop. Now with bob-dave as the
+	// incoming channel.
 	paths, err = ctx.findBlindedPaths(&blindedPathRestrictions{
-		minNumHops: 1,
-		maxNumHops: 2,
-		incomingChainedChannels: []uint64{
-			ctx.nodePairChannel("bob", "dave"),
-		},
+		minNumHops:              1,
+		maxNumHops:              1,
+		incomingChainedChannels: []uint64{2},
 	})
 	require.NoError(t, err)
 
-	// We expect the following paths:
-	//	- B, D
-	//	- F, B, D
-	// 	- E, B, D
+	// Expect only B->D path.
 	assertPaths(paths, []string{
 		"bob,dave",
-		"frank,bob,dave",
-		"eve,bob,dave",
-	})
-
-	// 6.3) Repeat the above test but instruct the function to never use
-	// bob. This should fail since bob owns one of the channels in the
-	// partially specified path.
-	_, err = ctx.findBlindedPaths(&blindedPathRestrictions{
-		minNumHops:      1,
-		maxNumHops:      2,
-		nodeOmissionSet: fn.NewSet(ctx.keyFromAlias("bob")),
-		incomingChainedChannels: []uint64{
-			ctx.nodePairChannel("bob", "dave"),
-		},
 	})
-	require.ErrorContains(t, err, "cannot simultaneously be included in "+
-		"the omission set and in the partially specified path")
 
-	// 6.4) Repeat it again but this time omit frank and demonstrate that
-	// the resulting set contains all the results from 6.2 except for the
-	// frank path.
+	// 8) Extend the search to include 2 hops other than the destination,
+	// with bob-dave as the incoming channel.
 	paths, err = ctx.findBlindedPaths(&blindedPathRestrictions{
-		minNumHops:      1,
-		maxNumHops:      2,
-		nodeOmissionSet: fn.NewSet(ctx.keyFromAlias("frank")),
-		incomingChainedChannels: []uint64{
-			ctx.nodePairChannel("bob", "dave"),
-		},
+		minNumHops:              1,
+		maxNumHops:              2,
+		incomingChainedChannels: []uint64{2},
 	})
 	require.NoError(t, err)
 
 	// We expect the following paths:
-	//	- B, D
+	//	- F, B, D
 	// 	- E, B, D
 	assertPaths(paths, []string{
-		"bob,dave",
+		"frank,bob,dave",
 		"eve,bob,dave",
 	})
 
-	// 6.5) Users may specify channels to nodes that do not signal route
-	// blinding (like A). So if we specify the A-D channel, we should get
-	// valid paths.
+	// 9) Extend the search even further and also increase the minimum path
+	// length, but this time with charlie-dave as the incoming channel.
 	paths, err = ctx.findBlindedPaths(&blindedPathRestrictions{
-		minNumHops: 1,
-		maxNumHops: 4,
-		incomingChainedChannels: []uint64{
-			ctx.nodePairChannel("dave", "alice"),
-		},
+		minNumHops:              2,
+		maxNumHops:              3,
+		incomingChainedChannels: []uint64{3},
 	})
 	require.NoError(t, err)
 
 	// We expect the following paths:
-	// 	- A, D
-	// 	- F, A, D
-	// 	- B, F, A, D
-	// 	- E, B, F, A, D
+	//	- E, C, D
+	// 	- B, E, C, D
 	assertPaths(paths, []string{
-		"alice,dave",
-		"frank,alice,dave",
-		"bob,frank,alice,dave",
-		"eve,bob,frank,alice,dave",
+		"eve,charlie,dave",
+		"bob,eve,charlie,dave",
 	})
 
-	// 6.6) Assert that an error is returned if a user accidentally tries
-	// to force a circular path.
+	// 10) Repeat the above test but instruct the function to never use
+	// charlie.
 	_, err = ctx.findBlindedPaths(&blindedPathRestrictions{
-		minNumHops: 2,
-		maxNumHops: 3,
-		incomingChainedChannels: []uint64{
-			ctx.nodePairChannel("dave", "alice"),
-			ctx.nodePairChannel("alice", "frank"),
-			ctx.nodePairChannel("frank", "bob"),
-			ctx.nodePairChannel("bob", "dave"),
-		},
-	})
-	require.ErrorContains(t, err, "circular route")
-
-	// 6.7) Test specifying a chain of incoming channels. We specify
-	// the following incoming list: [A->D, F->A].
-	paths, err = ctx.findBlindedPaths(&blindedPathRestrictions{
-		minNumHops: 1,
-		maxNumHops: 4,
-		incomingChainedChannels: []uint64{
-			ctx.nodePairChannel("dave", "alice"),
-			ctx.nodePairChannel("alice", "frank"),
-		},
+		minNumHops:              2,
+		maxNumHops:              3,
+		nodeOmissionSet:         fn.NewSet(ctx.keyFromAlias("charlie")),
+		incomingChainedChannels: []uint64{3},
 	})
-	require.NoError(t, err)
+	require.ErrorContains(t, err, "cannot simultaneously be included in "+
+		"the omission set and in the partially specified path")
 
-	// We expect the following paths:
-	// 	- F, A, D
-	// 	- B, F, A, D
-	// 	- E, B, F, A, D
-	assertPaths(paths, []string{
-		"frank,alice,dave",
-		"bob,frank,alice,dave",
-		"eve,bob,frank,alice,dave",
+	// 11) Test the circular route error.
+	_, err = ctx.findBlindedPaths(&blindedPathRestrictions{
+		minNumHops:              2,
+		maxNumHops:              3,
+		incomingChainedChannels: []uint64{2, 7, 6, 3},
 	})
+	require.ErrorContains(t, err, "a circular route cannot be specified")
 }