localchans: populate funding script for missing edges
What changed, and why it matters
This commit fixes a bug where Lightning Network channel edges created from a node's own channels were missing the 'funding script'—the on-chain Bitcoin script that locks funds in the channel. Without it, the routing graph builder could not properly track the channel's on-chain state (its ChainView), which could lead to stale routing data, incorrect channel visibility, or edge cases in pathfinding. The fix ensures the same helper used when opening real channels is used for these reconstructed edges.
Treat as a routine correctness fix. Review whether the missing FundingScript could have caused any exploitable routing or channel-state inconsistency, and include this commit in the next maintenance release. No immediate emergency response is indicated by the diff alone.
Security signals we found
Missing on-chain script in routing graph metadata
Inconsistent local channel graph state
Potential for routing decisions based on incomplete channel data
Fix aligns edge reconstruction with normal channel-opening logic
Evidence from the diff
In routing/localchans/manager.go, createEdge() now calls funding.MakeFundingScript(channel) and stores the result in models.ChannelEdgeInfo.FundingScript (wrapped in fn.Some). Previously this field was left empty when synthesizing a missing edge from a local channeldb.OpenChannel. The graph builder’s ChainView relies on the funding script to map the channel to its on-chain output, so omitting it could cause the local channel graph to be inconsistent with the chain. Tests were updated to assert the populated FundingScript.
Changed components
routing/localchans/manager.gorouting/localchans/manager_test.gograph builder ChainViewmodels.ChannelEdgeInfoInspect captured patch +25 / −5
diff --git a/routing/localchans/manager.go b/routing/localchans/manager.go
index b1d2811..a48486e 100644
--- a/routing/localchans/manager.go
+++ b/routing/localchans/manager.go
@@ -13,6 +13,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/fn/v2"
+ "github.com/lightningnetwork/lnd/funding"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire"
@@ -321,12 +322,19 @@ func (r *Manager) createEdge(channel *channeldb.OpenChannel,
shortChanID = channel.ZeroConfRealScid()
}
+ fundingScript, err := funding.MakeFundingScript(channel)
+ if err != nil {
+ return nil, nil, fmt.Errorf("unable to create funding "+
+ "script: %v", err)
+ }
+
info := &models.ChannelEdgeInfo{
- ChannelID: shortChanID.ToUint64(),
- ChainHash: channel.ChainHash,
- Features: lnwire.EmptyFeatureVector(),
- Capacity: channel.Capacity,
- ChannelPoint: channel.FundingOutpoint,
+ ChannelID: shortChanID.ToUint64(),
+ ChainHash: channel.ChainHash,
+ Features: lnwire.EmptyFeatureVector(),
+ Capacity: channel.Capacity,
+ ChannelPoint: channel.FundingOutpoint,
+ FundingScript: fn.Some(fundingScript),
}
copy(info.NodeKey1Bytes[:], nodeKey1Bytes)
diff --git a/routing/localchans/manager_test.go b/routing/localchans/manager_test.go
index a2e7164..5df344b 100644
--- a/routing/localchans/manager_test.go
+++ b/routing/localchans/manager_test.go
@@ -13,6 +13,8 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/discovery"
+ "github.com/lightningnetwork/lnd/fn/v2"
+ "github.com/lightningnetwork/lnd/funding"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc"
@@ -385,6 +387,10 @@ func TestCreateEdgeLower(t *testing.T) {
Index: 0,
},
}
+
+ fundingScript, err := funding.MakeFundingScript(channel)
+ require.NoError(t, err)
+
expectedInfo := &models.ChannelEdgeInfo{
ChannelID: 8,
ChainHash: channel.ChainHash,
@@ -399,6 +405,7 @@ func TestCreateEdgeLower(t *testing.T) {
remoteMultisigKey.SerializeCompressed()),
AuthProof: nil,
ExtraOpaqueData: nil,
+ FundingScript: fn.Some(fundingScript),
}
expectedEdge := &models.ChannelEdgePolicy{
ChannelID: 8,
@@ -473,6 +480,10 @@ func TestCreateEdgeHigher(t *testing.T) {
Index: 0,
},
}
+
+ fundingScript, err := funding.MakeFundingScript(channel)
+ require.NoError(t, err)
+
expectedInfo := &models.ChannelEdgeInfo{
ChannelID: 8,
ChainHash: channel.ChainHash,
@@ -487,6 +498,7 @@ func TestCreateEdgeHigher(t *testing.T) {
localMultisigKey.SerializeCompressed()),
AuthProof: nil,
ExtraOpaqueData: nil,
+ FundingScript: fn.Some(fundingScript),
}
expectedEdge := &models.ChannelEdgePolicy{
ChannelID: 8,
Why this scored 32/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.