Skip to content

[g175] graph/db: add v2 gossip support for channel policies#10542

Merged
ellemouton merged 13 commits intolightningnetwork:elle-g175Prep-basefrom
ellemouton:g175-db-6
Feb 12, 2026
Merged

[g175] graph/db: add v2 gossip support for channel policies#10542
ellemouton merged 13 commits intolightningnetwork:elle-g175Prep-basefrom
ellemouton:g175-db-6

Conversation

@ellemouton
Copy link
Copy Markdown
Collaborator

PR Description

This PR extends the graph database layer to support gossip v2 (1.75) channel policies, building on the foundation established in #10339, #10379, and #10380. This work is part of the larger gossip 1.75 implementation effort tracked in #10293.

Context

This PR completes the channel policy layer of the gossip v2 database preparation work:

Together, these changes enable LND to store and retrieve Taproot channel announcements with Schnorr signature aggregation, though v2 gossip remains disabled at the network level pending completion of validation and gossiper subsystem work.

Key Changes

Model Extensions

  • Add v2 fields to ChannelEdgePolicy: Version, LastBlockHeight, SecondPeer, DisableFlags, ExtraSignedFields
  • Add version-aware methods: IsNode1(), IsDisabled(), String()
  • New ChanEdgePolicyFromWire constructor for converting wire messages to policies

Database Operations

  • Update ForEachChannel, ForEachNodeChannel, and HasChannelEdge to accept gossip version parameter
  • Version-specific staleness checks in UpsertChannelPolicy:
    • V1: timestamp comparison with > (strict ordering)
    • V2: block height comparison with >= (allows same-block updates)
  • Version-aware reject cache for tracking rejected policies per version

Store Implementations

  • KVStore: Explicitly rejects v2 operations with ErrVersionNotSupportedForKVDB
  • SQLStore: Full v1 and v2 support with version-specific field mapping and policy reconstruction

Abstractions

  • Extended VersionedGraph with policy-specific methods that use configured gossip version
  • Refactored CachedEdgePolicy to use explicit boolean fields (HasMaxHTLC, IsNode1, IsDisabled) instead of bitfields for clarity and version-agnostic caching
  • Updated all call sites to explicitly specify GossipVersion1

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @ellemouton, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly upgrades the graph database layer to incorporate full support for Gossip V2 channel policies, building upon previous work to prepare for the 1.75 specification. The changes enable the storage and retrieval of Taproot channel announcements with Schnorr signature aggregation, laying crucial groundwork for future network-level activation of V2 gossip. The modifications ensure the database can correctly handle and differentiate between V1 and V2 policy updates, maintaining compatibility while introducing new capabilities.

Highlights

  • ChannelEdgePolicy Model Extension: The ChannelEdgePolicy model has been extended to support Gossip V2 (1.75) specific fields, including Version, LastBlockHeight, SecondPeer, DisableFlags, and ExtraSignedFields. New version-aware methods like IsNode1(), IsDisabled(), and String() have been added, along with a ChanEdgePolicyFromWire constructor to convert wire messages to policies.
  • Database Operations Update: Core database operations such as ForEachChannel, ForEachNodeChannel, and HasChannelEdge now accept a gossip version parameter. UpsertChannelPolicy includes version-specific staleness checks, using timestamp comparison for V1 and block height comparison for V2. The reject cache has also been made version-aware.
  • Store Implementation Differentiation: The KVStore explicitly rejects V2 operations with an ErrVersionNotSupportedForKVDB error, maintaining its V1-only scope. The SQLStore, however, now provides full support for both V1 and V2 policies, handling version-specific field mapping and policy reconstruction.
  • Abstraction Layer Refinement: The VersionedGraph interface has been extended with policy-specific methods that leverage the configured gossip version. The CachedEdgePolicy model has been refactored to use explicit boolean fields (HasMaxHTLC, IsNode1, IsDisabled) instead of bitfields for improved clarity and version-agnostic caching. All relevant call sites have been updated to explicitly specify GossipVersion1 where appropriate.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • discovery/gossiper.go
    • Updated handleChanUpdate to use the new models.ChanEdgePolicyFromWire constructor for creating channel edge policies from wire messages.
  • graph/builder.go
    • Modified ApplyChannelUpdate to utilize models.ChanEdgePolicyFromWire for policy creation.
    • Updated addEdge and updateEdge to pass GossipVersion1 to HasChannelEdge and introduced HasV1ChannelEdge for V1-specific checks.
    • Changed ForEachOutgoingChannels, IsKnownEdge, IsZombieEdge, and IsStaleEdgePolicy to explicitly pass GossipVersion1 or use HasV1ChannelEdge.
  • graph/builder_test.go
    • Added Version: lnwire.GossipVersion1 to ChannelEdgePolicy initializations in various tests.
    • Updated calls to HasChannelEdge to include the GossipVersion1 parameter.
  • graph/db/benchmark_test.go
    • Modified ForEachChannel calls in benchmarks to explicitly pass lnwire.GossipVersion1.
  • graph/db/graph.go
    • Updated ForEachNodeChannel, ForEachChannel, and HasChannelEdge methods to accept a lnwire.GossipVersion parameter.
    • Introduced HasV1ChannelEdge for V1-specific channel edge existence checks.
    • Added new HasChannelEdge, ForEachNodeChannel, and ForEachChannel methods to VersionedGraph that use its internal gossip version.
  • graph/db/graph_cache.go
    • Refactored AddChannel and UpdatePolicy to use the new explicit boolean fields (IsDisabled, IsNode1) on CachedEdgePolicy instead of bitfield checks.
    • Updated NewCachedPolicy to correctly map V1 and V2 ChannelEdgePolicy fields to the CachedEdgePolicy's new boolean flags.
  • graph/db/graph_test.go
    • Added Version field to ChannelEdgePolicy in test helper functions (createChannelEdge, newEdgePolicy).
    • Updated HasChannelEdge calls in tests to pass lnwire.GossipVersion1.
    • Introduced testEdgePolicyCRUD and testIncompleteChannelPolicies to test version-aware policy handling.
    • Modified compareEdgePolicies to be version-aware, comparing V1 and V2 specific fields appropriately.
  • graph/db/interfaces.go
    • Updated the Store interface for ForEachNodeChannel, ForEachChannel, and HasChannelEdge to include a lnwire.GossipVersion parameter.
    • Added HasV1ChannelEdge to the Store interface for V1-specific checks.
  • graph/db/kv_store.go
    • Modified ForEachChannel, ForEachNodeChannel, and updateEdgePolicy to return ErrVersionNotSupportedForKVDB if a version other than GossipVersion1 is used.
    • Renamed HasChannelEdge to HasV1ChannelEdge and introduced a new HasChannelEdge method that accepts a lnwire.GossipVersion parameter.
    • Updated rejectCache operations (remove, insert, get) to be version-aware using a new rejectCacheKey.
  • graph/db/models/cached_edge_policy.go
    • Replaced MessageFlags and ChannelFlags with explicit boolean fields: HasMaxHTLC, IsNode1, IsDisabled.
    • Updated NewCachedPolicy to correctly populate these new boolean fields based on the ChannelEdgePolicy's version.
  • graph/db/models/channel_edge_policy.go
    • Added new fields: Version, LastBlockHeight, SecondPeer, DisableFlags, ExtraSignedFields to support Gossip V2.
    • Introduced ChanEdgePolicyFromWire constructor to create ChannelEdgePolicy from lnwire.ChannelUpdate messages, handling both V1 and V2.
    • Updated IsNode1() and IsDisabled() methods to be version-aware, checking appropriate flags based on the policy's version.
    • Modified String() method to provide version-specific human-readable output.
  • graph/db/reject_cache.go
    • Extended rejectCacheEntry with upd1BlockHeight and upd2BlockHeight for V2 policies.
    • Added helper functions newRejectCacheEntryV1, newRejectCacheEntryV2, updateRejectCacheEntryV1, updateRejectCacheEntryV2 for version-specific entry creation and updates.
    • Introduced rejectCacheKey struct to make the cache version-aware, and updated get, insert, and remove methods to use this key.
  • graph/db/reject_cache_test.go
    • Updated calls to rejectCache methods (get, insert, remove) to include lnwire.GossipVersion1.
  • graph/db/sql_store.go
    • Modified updateEdgeCache to handle version-specific updates for rejectCacheEntry (timestamps for V1, block heights for V2).
    • Updated ForEachNodeChannel and ForEachChannel to accept a lnwire.GossipVersion parameter.
    • Refactored updateChanEdgePolicy to handle V1 and V2 policy fields and update logic, including version-specific staleness checks.
    • Renamed HasChannelEdge to HasV1ChannelEdge and implemented a new version-aware HasChannelEdge method.
    • Updated buildChanPolicy and related functions to be version-aware, correctly extracting and populating fields based on the policy version.
  • graph/notifications_test.go
    • Added Version: lnwire.GossipVersion1 to randEdgePolicy.
  • netann/chan_status_manager_test.go
    • Added Version: lnwire.GossipVersion1 to ChannelEdgePolicy in createEdgePolicies and ApplyChannelUpdate mock.
  • routing/localchans/manager.go
    • Added Version: lnwire.GossipVersion1 to ChannelEdgePolicy in createEdge.
  • routing/localchans/manager_test.go
    • Added Version: lnwire.GossipVersion1 to expectedEdge in TestCreateEdgeLower and TestCreateEdgeHigher.
  • routing/pathfind_test.go
    • Added Version: lnwire.GossipVersion1 to ChannelEdgePolicy in parseTestGraph and createTestGraphFromChannels.
  • routing/router_test.go
    • Added Version: lnwire.GossipVersion1 to ChannelEdgePolicy in TestAddEdgeUnknownVertexes and mockGraphBuilder.ApplyChannelUpdate.
  • routing/unified_edges.go
    • Updated amtInRange to use u.policy.HasMaxHTLC instead of u.policy.MessageFlags.HasMaxHtlc().
    • Updated getEdgeNetwork to use edge.policy.IsDisabled instead of edge.policy.IsDisabled() and edge.policy.HasMaxHTLC instead of edge.policy.MessageFlags.HasMaxHtlc().
  • routing/unified_edges_test.go
    • Updated CachedEdgePolicy initializations to use HasMaxHTLC instead of MessageFlags.
  • rpcserver.go
    • Changed the graph database access in DescribeGraph from r.server.graphDB to r.server.v1Graph to explicitly use the V1 graph.
  • server.go
    • Updated ForEachNodeChannel call in newServer to use s.v1Graph instead of s.graphDB.
  • sqldb/sqlc/graph.sql.go
    • Updated generated Go code to include Policy1BlockHeight, Policy1DisableFlags, Policy2BlockHeight, Policy2DisableFlags in various channel policy queries.
    • Modified UpsertEdgePolicyParams to include BlockHeight and DisableFlags.
    • Adjusted the UpsertEdgePolicy SQL query to handle version-specific update conditions (last_update for V1, block_height for V2).
  • sqldb/sqlc/queries/graph.sql
    • Modified SQL queries to select block_height and disable_flags for channel policies.
    • Updated the UpsertEdgePolicy SQL statement to insert and update block_height and disable_flags, and to apply version-specific WHERE clauses for staleness checks (comparing last_update for V1 and block_height for V2).
Activity
  • The pull request introduces significant changes to support Gossip V2 channel policies, indicating a planned and structured development effort.
  • The detailed PR description outlines the context, key changes, and how this PR fits into a larger implementation effort, suggesting thorough planning by the author.
  • The extensive modifications across multiple files, including core database logic, models, and tests, show a comprehensive approach to integrating V2 support.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

Auto-classified | 15 production files | ~1,004 lines changed (excl. tests/generated)

🔴 Critical (2 files)
  • server.go - Core server coordination
  • rpcserver.go - Core RPC server coordination
🟠 High (13 files)
  • discovery/gossiper.go - Gossip protocol implementation
  • graph/builder.go - Network graph builder
  • graph/db/graph.go - Graph database operations
  • graph/db/graph_cache.go - Graph caching layer
  • graph/db/interfaces.go - Graph database interfaces
  • graph/db/kv_store.go - Key-value store for graph data
  • graph/db/models/cached_edge_policy.go - Edge policy caching models
  • graph/db/models/channel_edge_policy.go - Channel edge policy models (93 additions)
  • graph/db/reject_cache.go - Rejection cache for graph updates
  • graph/db/sql_store.go - SQL-based graph storage (347 additions)
  • sqldb/sqlc/queries/graph.sql - Graph SQL queries
  • routing/localchans/manager.go - Local channel management for routing
  • routing/unified_edges.go - Unified edge handling for routing

Analysis

This PR adds v2 gossip support for channel policies, which is a significant enhancement to the Lightning Network graph management system.

Severity rationale:

  • Base severity: HIGH - The majority of changes are in graph/db/* (network graph maintenance) and routing/* (payment pathfinding), which are HIGH severity areas
  • Bumped to CRITICAL due to >500 production lines changed (~1,004 lines excluding tests and auto-generated files)
  • Additional critical touches: server.go and rpcserver.go, though changes are minimal (3 and 9 lines respectively)

Key areas of concern:

  1. Graph database modifications - Extensive changes to how channel policies are stored and retrieved (sql_store.go +347 lines)
  2. Wire protocol changes - Adding v2 gossip support affects how nodes communicate channel policies
  3. Caching layer updates - Changes to edge policy caching could impact performance and consistency
  4. SQL schema evolution - New queries in graph.sql that modify database access patterns

Recommended review focus:

  • Backward compatibility with v1 gossip
  • Database migration path (if applicable)
  • Edge cases in policy caching with mixed v1/v2 nodes
  • Performance implications of SQL store changes

To override, add a severity-override-{critical,high,medium,low} label.

@ellemouton ellemouton self-assigned this Feb 3, 2026
@ellemouton
Copy link
Copy Markdown
Collaborator Author

@claude review this

@codex review this

@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

Manual classification | 28 files | 1,674 lines changed

🔴 Critical (2 files)
  • server.go - Core server coordination
  • rpcserver.go - Core RPC server coordination
🟠 High (12 files)
  • graph/db/graph.go - Network graph database maintenance
  • graph/db/kv_store.go - Graph key-value store operations
  • graph/db/sql_store.go - Graph SQL store operations (347 additions, 96 deletions)
  • graph/db/interfaces.go - Graph database interfaces
  • graph/db/graph_cache.go - Graph caching layer
  • graph/db/reject_cache.go - Rejection cache logic
  • graph/db/models/channel_edge_policy.go - Channel policy models (93 additions)
  • graph/db/models/cached_edge_policy.go - Cached policy models
  • graph/builder.go - Graph builder coordination
  • discovery/gossiper.go - Gossip protocol handling
  • sqldb/sqlc/graph.sql.go - Auto-generated SQL code (138 additions)
  • sqldb/sqlc/queries/graph.sql - SQL schema changes (62 additions, 13 deletions)
🟡 Medium (3 files)
  • routing/unified_edges.go - Unified edge handling
  • routing/localchans/manager.go - Local channel management
  • netann/chan_status_manager_test.go - Test file
🟢 Low (11 files)
  • docs/release-notes/release-notes-0.21.0.md - Release notes
  • graph/db/benchmark_test.go - Benchmark tests
  • graph/db/graph_test.go - Graph tests (223 additions, 103 deletions)
  • graph/db/graph_cache_test.go - Cache tests
  • graph/db/reject_cache_test.go - Rejection cache tests
  • graph/builder_test.go - Builder tests
  • graph/notifications_test.go - Notification tests
  • routing/localchans/manager_test.go - Manager tests
  • routing/pathfind_test.go - Pathfinding tests
  • routing/router_test.go - Router tests
  • routing/unified_edges_test.go - Unified edges tests

Analysis

This PR adds v2 gossip support for channel policies and is classified as CRITICAL due to:

  1. Core Server Impact: Changes to server.go and rpcserver.go affect core daemon coordination and RPC handling, requiring expert review.

  2. Database Schema Changes: Modifications to SQL schema (sqldb/sqlc/queries/graph.sql) with 62 additions and 13 deletions are always critical, as they affect data persistence and migration paths.

  3. Extensive Graph Database Changes: The PR touches 8 graph/db files with significant logic changes:

    • sql_store.go: 347 additions, 96 deletions
    • channel_edge_policy.go: 93 additions (model changes)
    • graph_test.go: 223 additions (substantial test expansion)
  4. Scope Triggers:

    • ✓ >20 files changed (excluding tests: ~17 non-test files, borderline)
    • ✓ >500 lines changed (excluding tests/auto-generated: ~800+ lines)
    • ✓ Multiple distinct critical/high packages (server, rpcserver, graph/db, sqldb, routing, discovery)
  5. Protocol Impact: This adds v2 gossip support, which affects:

    • How channel policies are stored and retrieved (graph/db)
    • How gossip messages are processed (discovery/gossiper.go)
    • How routing decisions are made (routing/* files)
    • Core server initialization and RPC handling

Concerns:

  • Database migration path needs careful review
  • Backward compatibility with v1 gossip
  • Concurrent access patterns in graph cache
  • Impact on routing performance with new policy models

This requires expert review from maintainers familiar with the Lightning gossip protocol, graph database layer, and SQL schema evolution.


To override, add a severity-override-{critical,high,medium,low} label.

@claude
Copy link
Copy Markdown

claude Bot commented Feb 3, 2026

Claude encountered an error —— View job

Command failed: git fetch origin --depth=20 g175-db-6

I'll analyze this and get back to you.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This PR adds support for gossip v2 channel policies to the graph database layer. This is a significant change that touches many parts of the codebase, including the database schema, data models, and graph traversal logic. The changes are well-structured, with clear separation between v1 and v2 logic, especially in the sql_store and the new version-aware model constructors. The refactoring of CachedEdgePolicy to use boolean fields instead of bitmasks is a nice improvement for readability. My review focuses on ensuring documentation for new and modified exported functions is sufficiently detailed as per the project's style guide. Overall, this is a solid step towards full gossip v2 support.

Comment thread graph/db/graph.go
return c.db.DisabledChannelIDs()
}

// HasV1ChannelEdge returns true if the database knows of a channel edge.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment for HasV1ChannelEdge is a bit brief. To align with the style guide's emphasis on detailed comments for exported functions, consider expanding it to explain what the function does and what its return values represent, similar to the comment for this function in the Store interface.

Suggested change
// HasV1ChannelEdge returns true if the database knows of a channel edge.
// HasV1ChannelEdge returns true if the database knows of a v1 channel edge
// with the passed channel ID, and false otherwise. If an edge with that
// ID is found within the graph, then two time stamps representing the
// last time the edge was updated for both directed edges are returned
// along with the boolean. If it is not found, then the zombie index is
// checked and its result is returned as the second boolean.
References
  1. Exported functions require detailed comments for the caller. (link)

Comment thread graph/db/graph.go
return c.db.HasV1ChannelEdge(chanID)
}

// HasChannelEdge returns true if the database knows of a channel edge.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment for HasChannelEdge is a bit brief. To align with the style guide's emphasis on detailed comments for exported functions, consider expanding it to explain what the function does and what its return values represent, similar to the comment for this function in the Store interface.

// HasChannelEdge returns true if the database knows of a channel edge
// with the passed channel ID and gossip version, and false otherwise.
// If it is not found, then the zombie index is checked and its result
// is returned as the second boolean.
References
  1. Exported functions require detailed comments for the caller. (link)

Comment thread graph/db/graph.go
return c.db.HasChannelEdge(c.v, chanID)
}

// ForEachNodeChannel iterates through all channels of the given node.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment for ForEachNodeChannel is a bit brief. To align with the style guide's emphasis on detailed comments for exported functions, consider expanding it to explain what the function does, its parameters, and what is passed to the callback, similar to the comment for this function in the Store interface.

// ForEachNodeChannel iterates through all channels of the given node for the
// graph's gossip version, 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.
//
// NOTE: Unknown policies are passed into the callback as nil values.
References
  1. Exported functions require detailed comments for the caller. (link)

Comment thread graph/db/graph.go
return c.db.ForEachNodeChannel(ctx, c.v, nodePub, cb, reset)
}

// ForEachChannel iterates through all channel edges stored within the graph.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment for ForEachChannel is a bit brief. To align with the style guide's emphasis on detailed comments for exported functions, consider expanding it to explain what the function does and what is passed to the callback, similar to the comment for this function in the Store interface.

// ForEachChannel iterates through all channel edges stored within the graph for
// the graph's gossip version.
//
// 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.
References
  1. Exported functions require detailed comments for the caller. (link)

@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

PR #10542 | 30 files | 1,679 lines changed (1,009 non-test/non-generated)

🔴 Critical (2 files)
  • server.go - Core server coordination, integrating v2 gossip support
  • rpcserver.go - Core RPC server coordination for policy updates
🟠 High (13 files)
  • graph/db/graph.go - Network graph maintenance, v2 policy storage
  • graph/db/graph_cache.go - Graph caching layer modifications
  • graph/db/interfaces.go - Graph database interface changes
  • graph/db/kv_store.go - Key-value store for graph data
  • graph/db/models/cached_edge_policy.go - Edge policy caching models
  • graph/db/models/channel_edge_policy.go - Core channel edge policy models (93 additions)
  • graph/db/reject_cache.go - Reject cache for invalid policies
  • graph/db/sql_store.go - SQL storage layer (347 additions - largest change)
  • sqldb/sqlc/queries/graph.sql - SQL queries for graph operations
  • graph/builder.go - Network graph builder logic
  • discovery/gossiper.go - Gossip protocol handling
  • routing/unified_edges.go - Routing edge unification
  • routing/localchans/manager.go - Local channel management
🟢 Low (1 file)
  • docs/release-notes/release-notes-0.21.0.md - Release notes documentation
📝 Test/Generated Files (14 files - excluded from severity calculation)

Test files: graph/db/graph_test.go, graph/db/graph_cache_test.go, graph/db/reject_cache_test.go, graph/db/benchmark_test.go, graph/builder_test.go, graph/notifications_test.go, routing/unified_edges_test.go, routing/pathfind_test.go, routing/router_test.go, routing/localchans/manager_test.go, autopilot/prefattach_test.go, netann/chan_status_manager_test.go, lnrpc/invoicesrpc/addinvoice_test.go

Auto-generated: sqldb/sqlc/graph.sql.go

Analysis

This PR adds v2 gossip support for channel policies, which is a critical change that touches:

  1. Core Server Files: Changes to server.go and rpcserver.go immediately elevate this to critical severity, as these coordinate the entire LND server operation.

  2. Extensive Graph Database Changes: The PR modifies 13 high-severity files across the graph database layer, including major changes to:

    • SQL storage layer (347 additions in sql_store.go)
    • Channel edge policy models (93 additions)
    • Graph persistence and caching mechanisms
  3. Size Trigger: With 1,009 non-test/non-generated lines changed (exceeding the 500-line threshold), this PR would be bumped up one severity level even without the critical files.

  4. Protocol Addition: Adding v2 gossip support is a protocol-level change that affects how channel policies are stored, validated, and propagated across the network. This requires expert review to ensure:

    • Backward compatibility with v1 gossip
    • Correct SQL schema changes and data migrations
    • Proper handling in the gossip protocol and routing logic
    • No edge cases in policy storage/retrieval

Recommendation: This PR requires thorough review by engineers familiar with the gossip protocol, graph database architecture, and core server coordination logic.


To override, add a severity-override-{critical,high,medium,low} label.

@saubyk saubyk added this to v0.21 Feb 3, 2026
@saubyk saubyk moved this to In progress in v0.21 Feb 3, 2026
@ellemouton
Copy link
Copy Markdown
Collaborator Author

@claude review this

@claude
Copy link
Copy Markdown

claude Bot commented Feb 4, 2026

Claude encountered an error —— View job

Command failed: git fetch origin --depth=20 g175-db-6

I'll analyze this and get back to you.

Extend channel policy queries and structs to support v2-specific fields:

- Add BlockHeight field to track the block height for v2 policy updates.
- Add DisableFlags field for v2 channel disable messages.

Both fields are nullable (sql.NullInt64/Int16) to maintain backwards
compatibility with v1 channels. The fields are initialized as null in
updateChanEdgePolicy and threaded through all policy-related queries
(GetChannelBySCIDWithPolicies, ListChannelsByNodeID, UpsertEdgePolicy,
etc.) and the extractChannelPolicies helper.

This commit includes both the hand-written SQL query updates and the
corresponding sqlc-generated Go code.
@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

PR #10542 by @ellemouton | 30 files | 1,682 lines changed

🔴 Critical (2 files)
  • server.go - Core server coordination with v2 gossip integration
  • rpcserver.go - Core RPC server modifications for channel policy queries
🟠 High (11 files)
  • discovery/gossiper.go - Gossip protocol changes for v2 channel policies
  • graph/builder.go - Network graph builder modifications
  • graph/db/graph.go - Core graph database operations with v2 policy support
  • graph/db/graph_cache.go - Graph caching layer updates
  • graph/db/interfaces.go - Graph database interface changes
  • graph/db/kv_store.go - Key-value storage for graph data
  • graph/db/models/cached_edge_policy.go - Cached edge policy model changes
  • graph/db/models/channel_edge_policy.go - Channel edge policy model with v2 support (93 additions)
  • graph/db/reject_cache.go - Reject cache implementation updates
  • graph/db/sql_store.go - SQL storage implementation (347 additions, significant refactoring)
  • routing/unified_edges.go - Unified edges for routing
🟡 Medium (3 files)
  • sqldb/sqlc/queries/graph.sql - SQL query definitions for graph operations
  • routing/localchans/manager.go - Local channel manager integration
  • docs/release-notes/release-notes-0.21.0.md - Release notes update
🟢 Low/Test (14 files)

Test files and auto-generated code (excluded from severity calculation)

Analysis

This PR adds v2 gossip support for channel policies, which is a substantial architectural change touching critical infrastructure:

Critical Concerns:

  1. Core Server Integration: Changes to server.go and rpcserver.go affect the main coordination and RPC serving logic
  2. Graph Database Refactoring: Extensive modifications to the graph database layer (sql_store.go with 347 additions) that handles channel policy persistence
  3. Wire Format Changes: Introduction of v2 channel policy support affects how the node stores and retrieves channel policies from the database

Risk Factors:

  • The PR touches the core graph database abstraction layer, which is used throughout the routing and gossip subsystems
  • Changes span multiple critical packages: graph/db, discovery, routing, and server coordination
  • Large line count in graph/db/sql_store.go (347 additions) suggests significant refactoring of database operations
  • Database schema and query changes in SQL layer could affect data integrity

Recommendation:
This PR requires expert review from maintainers familiar with:

  • The graph database architecture and SQL store implementation
  • The gossip protocol and channel policy announcement handling
  • Database migration patterns and backward compatibility considerations

The changes appear well-structured based on the affected files, but the scope and depth warrant careful scrutiny given the critical nature of channel policy storage and retrieval.


To override, add a severity-override-{critical,high,medium,low} label.

@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

Base: HIGH | 30 files | 1684 lines changed | Bumped due to: >20 files + substantial graph database changes

🟠 High (11 files)
  • graph/db/sql_store.go - SQL store for graph data (347 additions, 96 deletions) - major refactoring for v2 gossip support
  • graph/db/models/channel_edge_policy.go - Channel policy models (93 additions) - new v2 policy structures
  • graph/db/graph.go - Network graph database operations (42 additions) - core graph data handling
  • graph/db/kv_store.go - Key-value store for graph data (59 additions) - storage layer changes
  • graph/db/reject_cache.go - Reject cache logic (87 additions) - cache management for rejected updates
  • graph/db/interfaces.go - Graph database interfaces (18 additions) - interface modifications
  • graph/db/models/cached_edge_policy.go - Cached policy models (29 additions)
  • graph/builder.go - Graph builder coordination (16 additions)
  • discovery/gossiper.go - Gossip protocol handling (6 additions, 13 deletions)
  • sqldb/sqlc/graph.sql.go - Auto-generated SQL code (138 additions)
  • sqldb/sqlc/queries/graph.sql - SQL queries for graph (62 additions)
🟡 Medium (2 files)
  • server.go - Core server coordination (2 additions, 1 deletion) - minor integration changes
  • rpcserver.go - RPC server (5 additions, 4 deletions) - minor integration changes
🟢 Low (17 files)

Test files and documentation:

  • autopilot/prefattach_test.go
  • graph/builder_test.go
  • graph/db/benchmark_test.go
  • graph/db/graph_cache_test.go
  • graph/db/graph_test.go
  • graph/db/reject_cache_test.go
  • graph/notifications_test.go
  • lnrpc/invoicesrpc/addinvoice_test.go
  • netann/chan_status_manager_test.go
  • routing/localchans/manager_test.go
  • routing/pathfind_test.go
  • routing/router_test.go
  • routing/unified_edges_test.go
  • routing/unified_edges.go (3 additions, minor)
  • routing/localchans/manager.go (1 addition, minor)
  • graph/db/graph_cache.go (6 additions, minor)
  • docs/release-notes/release-notes-0.21.0.md

Analysis

This PR adds v2 gossip support for channel policies, involving substantial changes to the graph database layer. While the individual files are classified as HIGH severity (graph/, discovery/, sqldb/*), the PR meets multiple criteria for bumping to CRITICAL:

  1. Scope: 30 total files changed (>20 file threshold)
  2. Complexity: Significant refactoring of graph database storage with ~700 non-test/non-generated lines changed
  3. Core Infrastructure: Changes affect how channel policies are stored and retrieved, which is fundamental to routing decisions
  4. Multi-layer Impact: Touches SQL storage, KV storage, caching layers, and gossip protocol handling

Key Concerns:

  • The sql_store.go changes (347 additions) represent major restructuring of graph data persistence
  • New v2 policy structures in channel_edge_policy.go (93 additions) alter core data models
  • Changes span storage backends (SQL + KV), cache layers, and network protocol handling
  • Network graph integrity is critical for routing; bugs could lead to payment failures or incorrect pathfinding

Recommendation: This PR requires expert review from someone deeply familiar with lnd's graph database architecture, gossip protocol implementation, and the v2 gossip specification.


To override, add a severity-override-{critical,high,medium,low} label.

@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

v2 gossip support | 30 files | 1,706 lines changed

🔴 Critical (2 files)
  • server.go - Core server coordination (2 lines)
  • rpcserver.go - Core RPC server coordination (9 lines)
🟠 High (11 files)
  • discovery/gossiper.go - Gossip protocol implementation (19 lines)
  • graph/builder.go - Network graph maintenance (37 lines)
  • graph/db/graph.go - Network graph database operations (50 lines)
  • graph/db/graph_cache.go - Graph caching layer (12 lines)
  • graph/db/interfaces.go - Graph database interfaces (25 lines)
  • graph/db/kv_store.go - Key-value storage backend (80 lines)
  • graph/db/models/cached_edge_policy.go - Edge policy caching (49 lines)
  • graph/db/models/channel_edge_policy.go - Channel edge policy model (97 lines)
  • graph/db/reject_cache.go - Rejection cache implementation (98 lines)
  • graph/db/sql_store.go - SQL storage backend (443 lines)
  • sqldb/sqlc/queries/graph.sql - SQL queries for graph operations (75 lines)
🟡 Medium (2 files)
  • routing/localchans/manager.go - Local channel manager (1 line)
  • routing/unified_edges.go - Unified edge representation (6 lines)
🟢 Low (1 file)
  • docs/release-notes/release-notes-0.21.0.md - Release notes documentation (3 lines)
🧪 Test files (14 files, excluded from severity calculation)
  • autopilot/prefattach_test.go
  • graph/builder_test.go
  • graph/db/benchmark_test.go
  • graph/db/graph_cache_test.go
  • graph/db/graph_test.go
  • graph/db/reject_cache_test.go
  • graph/notifications_test.go
  • lnrpc/invoicesrpc/addinvoice_test.go
  • netann/chan_status_manager_test.go
  • routing/localchans/manager_test.go
  • routing/pathfind_test.go
  • routing/router_test.go
  • routing/unified_edges_test.go
  • sqldb/sqlc/graph.sql.go (auto-generated)

Analysis

This PR adds v2 gossip support for channel policies, which is a significant architectural change touching critical infrastructure:

Why CRITICAL:

  1. Critical Package Involvement: Touches both server.go and rpcserver.go, the core coordination components
  2. Substantial Graph Database Changes: Major modifications to the network graph storage layer (graph/db/*) with 443 lines changed in sql_store.go alone
  3. Protocol-Level Change: Implements v2 gossip support, which affects how the node communicates channel policy updates across the Lightning Network
  4. High Line Count: Over 1,000 lines of production code changed (excluding tests and auto-generated files)
  5. Multiple Distinct Systems: Affects server coordination, gossip protocol, graph database, and routing components

Concerns:

  • Database schema/query changes require careful review for correctness and performance
  • Gossip protocol changes must maintain compatibility with the network
  • Graph cache invalidation logic needs verification
  • Integration between v1 and v2 gossip needs thorough testing

Recommendation: This PR requires expert review from engineers familiar with:

  • Lightning Network gossip protocol specifications
  • LND's graph database architecture
  • Channel policy routing and forwarding logic

To override, add a severity-override-{critical,high,medium,low} label.

@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

#10542 | 30 files | 1,270 additions / 435 deletions

🔴 Critical (9 files)
  • graph/db/graph.go - Core channel graph maintenance operations
  • graph/db/kv_store.go - KV database storage implementation with v2 rejection logic
  • graph/db/sql_store.go - SQL database implementation with full v1/v2 policy support (349 additions)
  • graph/db/interfaces.go - Database interface definitions extended for gossip versioning
  • sqldb/sqlc/graph.sql.go - Auto-generated SQL code for graph operations
  • sqldb/sqlc/queries/graph.sql - Raw SQL queries for channel policy CRUD operations
  • server.go - Core server coordination changes
  • rpcserver.go - RPC server updates for versioned graph access
  • discovery/gossiper.go - Gossip protocol integration
🟠 High (3 files)
  • graph/db/models/channel_edge_policy.go - Core policy model extended with v2 fields (93 additions)
  • graph/db/models/cached_edge_policy.go - Edge policy caching refactored for version-agnostic storage
  • graph/db/reject_cache.go - Version-aware reject cache for tracking rejected policies (87 additions)
🟡 Medium (6 files)
  • graph/builder.go - Graph builder integration with versioned operations
  • routing/unified_edges.go - Routing edge abstractions
  • routing/localchans/manager.go - Local channel management
  • docs/release-notes/release-notes-0.21.0.md - Release notes
🟢 Low (12 test files)

Test files: graph/db/graph_test.go, graph/db/graph_cache_test.go, graph/db/reject_cache_test.go, graph/db/benchmark_test.go, graph/builder_test.go, routing/router_test.go, routing/pathfind_test.go, routing/unified_edges_test.go, routing/localchans/manager_test.go, autopilot/prefattach_test.go, graph/notifications_test.go, netann/chan_status_manager_test.go, lnrpc/invoicesrpc/addinvoice_test.go

Analysis

This PR receives CRITICAL severity based on the following factors:

  1. Core Database Infrastructure: Changes to graph/db/* files constitute modifications to the channel graph database layer, which is fundamental to LND's network graph persistence and retrieval. The SQL store implementation alone adds 349 lines of new logic for v2 policy handling.

  2. Multiple Critical Subsystems: The PR touches critical server coordination files (server.go, rpcserver.go), graph database implementations (KV and SQL stores), and database query layers.

  3. Schema and Interface Evolution: Extends core database interfaces and models to support dual-version (v1/v2) channel policies, including new fields for Taproot channels and Schnorr signatures. This type of foundational change requires expert review to ensure backward compatibility and correct version handling.

  4. Scale and Complexity:

    • 30 files changed (15+ non-test files)
    • ~900+ lines of non-test, non-generated code changes
    • Touches graph database, routing, discovery, and RPC layers
  5. Database Migration Risk: While not a traditional migration, this PR introduces parallel v1/v2 storage paths and version-specific staleness checks that could affect channel policy persistence and retrieval correctness.

Key Review Areas:

  • Version-aware policy staleness logic (timestamp vs block height comparison)
  • SQL query correctness for v2 field mapping
  • KV store rejection behavior for v2 operations
  • Reject cache versioning to prevent cross-version conflicts
  • Graph traversal methods (ForEachChannel, etc.) correctly filtering by version

This is part of the larger gossip 1.75 implementation (#10293) and builds on database foundation work from PRs #10339, #10379, and #10380.


To override, add a severity-override-{critical,high,medium,low} label.

@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

g175: Add v2 gossip support for channel policies | 30 files | 1,270 additions / 435 deletions

🔴 Critical (2 files)
  • server.go - Core server coordination (2 additions / 1 deletion)
  • rpcserver.go - Core RPC server coordination (5 additions / 4 deletions)
🟠 High (22 files)

Discovery (1 file)

  • discovery/gossiper.go - Gossip protocol message handling (6 additions / 13 deletions)

Routing (6 files)

  • routing/localchans/manager.go - Local channel management (1 addition)
  • routing/localchans/manager_test.go - Tests (3 additions)
  • routing/pathfind_test.go - Pathfinding tests (5 additions)
  • routing/router_test.go - Router tests (11 additions / 13 deletions)
  • routing/unified_edges.go - Unified edge handling (3 additions / 3 deletions)
  • routing/unified_edges_test.go - Tests (2 additions / 2 deletions)

Graph (13 files)

  • graph/builder.go - Graph builder (16 additions / 21 deletions)
  • graph/builder_test.go - Tests (19 additions / 11 deletions)
  • graph/db/benchmark_test.go - Benchmarks (44 additions / 42 deletions)
  • graph/db/graph.go - Core graph database operations (42 additions / 8 deletions)
  • graph/db/graph_cache.go - Graph caching (6 additions / 6 deletions)
  • graph/db/graph_cache_test.go - Tests (19 additions / 14 deletions)
  • graph/db/graph_test.go - Tests (224 additions / 103 deletions)
  • graph/db/interfaces.go - Database interfaces (18 additions / 7 deletions)
  • graph/db/kv_store.go - Key-value store operations (59 additions / 21 deletions)
  • graph/db/models/cached_edge_policy.go - Cached edge policy model (29 additions / 20 deletions)
  • graph/db/models/channel_edge_policy.go - Channel edge policy model (93 additions / 4 deletions)
  • graph/db/reject_cache.go - Reject cache implementation (87 additions / 11 deletions)
  • graph/db/reject_cache_test.go - Tests (13 additions / 8 deletions)

SQL Database (2 files)

  • sqldb/sqlc/graph.sql.go - Auto-generated SQL code (138 additions / 13 deletions)
  • sqldb/sqlc/queries/graph.sql - SQL queries for graph operations (62 additions / 13 deletions)
🟡 Medium (4 files)
  • graph/db/sql_store.go - SQL store implementation (349 additions / 96 deletions)
  • graph/notifications_test.go - Tests (1 addition)
  • autopilot/prefattach_test.go - Tests (2 additions)
  • netann/chan_status_manager_test.go - Tests (3 additions)
🟢 Low (2 files)
  • docs/release-notes/release-notes-0.21.0.md - Release notes (2 additions / 1 deletion)
  • lnrpc/invoicesrpc/addinvoice_test.go - Tests (6 additions)

Analysis

This PR achieves CRITICAL severity due to modifications in core coordination files (server.go, rpcserver.go), which are central to lnd's operation and require expert review.

Additionally, the PR makes substantial changes to the graph database layer:

  • 349 lines added to graph/db/sql_store.go implementing v2 gossip support
  • 93 lines added to graph/db/models/channel_edge_policy.go extending the channel policy model
  • 62 lines added to SQL queries in sqldb/sqlc/queries/graph.sql
  • Multiple changes across graph caching, reject cache, and KV store operations

The scope includes:

  1. Core server/RPC integration points for v2 gossip
  2. Network graph database schema and query modifications
  3. Channel policy storage and retrieval logic
  4. Gossip message handling and routing integration

Given the combination of critical server files and extensive graph database modifications that affect how channel policies are stored and retrieved (a foundational aspect of Lightning network operation), this PR requires careful expert review to ensure data integrity and backward compatibility.


To override, add a severity-override-{critical,high,medium,low} label.

Copy link
Copy Markdown
Collaborator

@bhandras bhandras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! ☕

return c.ChannelFlags.IsDisabled()
}

return !c.DisableFlags.IsEnabled()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: perhaps worth updating the godoc a bit, to flag that this means that either incoming or outgoing is disabled. Maybe we could also add IsIncomingDisabled() and IsOutgoingDisabled() too (similarly for the cached policy).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool - updated the doc. for now, not including IsIncomingDisabled or IsOutgoingDisabled since 1) we dont use that anywhere yet and 2) we dnot have that data for v1 channels so would need to return errors at the moment. Which we can defs add later - but i think for now while we are just squeezing in the two, we want to keep the diff as minimal as possible

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, the ChanUpdateDisableFlags type does have IncomingDisabled() and OutgoingDisabled() already

Comment thread graph/db/models/channel_edge_policy.go Outdated
return &ChannelEdgePolicy{
Version: lnwire.GossipVersion2,
SigBytes: upd.Signature.Val.ToSignatureBytes(),
ChannelID: upd.ShortChannelID.Val.ToUint64(),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: doesn't seem like a bug, but should we consistently fill this with scid like above?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great catch! does actually seem like a bug given the comment at the call-site in the gossiper:

// We'll use chanInfo.ChannelID rather than the peer-supplied
	// ShortChannelID in the ChannelUpdate to avoid the router having to
	// lookup the stored SCID. If we're sending the update, we'll always
	// use the SCID stored in the database rather than a potentially
	// 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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that comment is really confusing

c.MessageFlags, c.ChannelFlags, c.LastUpdate)
}

return fmt.Sprintf("ChannelID=%v, Node1=%v, DisableFlags=%v, "+
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should we print the disable status directionally here too?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea! what ive done is to instead update the String method on the lnwire.ChanUpdateDisabledFlags type instead to return this info

Comment thread graph/db/graph.go
// passed channel ID and this graph's gossip version, and false otherwise. If it
// is not found, then the zombie index is checked and its result is returned as
// the second boolean.
func (c *VersionedGraph) HasChannelEdge(chanID uint64) (bool, bool, error) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not sure where to put this comment, but do we want the same versioning for the channelCache as we've added for the rejectCache?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes absolutely - the cache is, however, only ever read/used in ChanUpdatesInHorizon - which we have not "versioned" yet in this PR. So leaving this for the coming follow up PRs :)

@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

v2 gossip support | 31 files | 1,734 lines changed

🔴 Critical (3 files)
  • lnwire/channel_update_2.go - Lightning wire protocol message implementation for v2 channel updates
  • server.go - Core server coordination changes
  • rpcserver.go - Core RPC server modifications
🟠 High (14 files)
  • discovery/gossiper.go - Gossip protocol handling
  • graph/builder.go - Network graph maintenance
  • graph/db/graph.go - Graph database core operations
  • graph/db/kv_store.go - Graph key-value storage
  • graph/db/sql_store.go - Graph SQL storage (348 additions)
  • graph/db/graph_cache.go - Graph caching layer
  • graph/db/interfaces.go - Graph database interfaces
  • graph/db/reject_cache.go - Rejection cache for invalid updates
  • graph/db/models/channel_edge_policy.go - Channel policy data models
  • graph/db/models/cached_edge_policy.go - Cached policy models
  • routing/localchans/manager.go - Local channel management
  • routing/unified_edges.go - Unified edge handling
  • sqldb/sqlc/queries/graph.sql - SQL queries for graph data
  • sqldb/sqlc/graph.sql.go - Generated SQL code
🟡 Medium/Low (14 files)
  • Test files: *_test.go (13 files)
  • docs/release-notes/release-notes-0.21.0.md - Documentation

Analysis

This PR introduces v2 gossip support for channel policies, which is a protocol-level change requiring expert review. The severity is CRITICAL because:

  1. Wire Protocol Changes: Modifies lnwire/channel_update_2.go, introducing new Lightning Network protocol messages. Any bugs here could cause network-wide compatibility issues or parsing failures.

  2. Core Infrastructure: Touches both server.go and rpcserver.go, which are central coordination points in lnd. Changes here affect the entire daemon's operation.

  3. Extensive Graph Database Modifications: Significant changes across the graph storage layer (graph/db/*), including SQL schema updates and caching logic. The sql_store.go file alone has 348 additions, implementing new persistence for v2 channel updates.

  4. Multi-Component Impact: This PR spans wire protocol, gossip, routing, and database layers - touching multiple critical systems that must work together correctly.

  5. Protocol Evolution Risk: Introducing v2 gossip support while maintaining backward compatibility with v1 creates complex state management requirements and potential edge cases.

Key Review Areas:

  • Validation of new wire message format in channel_update_2.go
  • Database migration safety and rollback capability
  • Backward compatibility with nodes not supporting v2
  • Caching consistency between v1 and v2 policies
  • Gossip propagation logic for mixed v1/v2 network

To override, add a severity-override-{critical,high,medium,low} label.

@ellemouton
Copy link
Copy Markdown
Collaborator Author

@claude review this

@claude
Copy link
Copy Markdown

claude Bot commented Feb 12, 2026

Claude finished @ellemouton's task in 2m 57s —— View job


Code Review Complete

Reviewing PR #10542: v2 gossip support for channel policies


Summary

This PR successfully extends the graph database layer to support gossip v2 (1.75) channel policies. The implementation is well-structured with clear version separation and follows good architectural patterns. I've identified several areas that merit attention, though no critical blockers were found.

Overall Assessment: ✅ Solid implementation, ready for merge with minor considerations


Detailed Review

1. Wire Protocol Changes (lnwire/channel_update_2.go)

Well-implemented TLV-based message structure

Observations:

  • Proper use of optional fields (SecondPeer, InboundFee)
  • Default value handling for omitted fields (lines 143-168)
  • Signature forcing to Schnorr (line 129) - correct for v2

Minor Issue: Line 185 has a typo in comment: "genisis" → "genesis"

2. Model Extensions (graph/db/models/)

channel_edge_policy.go

Version-aware methods are well-designed

Good:

  • ChanEdgePolicyFromWire centralizes wire→model conversion (lines 102-150)
  • Version-specific IsNode1() and IsDisabled() methods (lines 152-171)
  • Clean separation of v1 and v2 fields

Concern:
The IsDisabled() method for v2 returns true only if BOTH incoming and outgoing are disabled (line 170). This is documented in the updated godoc, but differs from v1 behavior where a single flag determines disabled status.

// For v2:
return !c.DisableFlags.IsEnabled()  // True only if c.DisableFlags != 0

// For v1:  
return c.ChannelFlags.IsDisabled()  // True if ChanUpdateDisabled bit set

This semantic difference should be clearly understood by callers. The updated godoc helps, but consider if this warrants a separate method like IsFullyDisabled() for v2 to make the distinction explicit.

cached_edge_policy.go

Excellent refactoring

Replacing bitfields with explicit booleans (HasMaxHTLC, IsNode1, IsDisabled) greatly improves code clarity and makes the cache version-agnostic. Well done!

3. Database Layer

SQL Store (graph/db/sql_store.go)

Comprehensive v2 support with version-specific logic

Strong points:

  • Version-aware field mapping in updateChanEdgePolicy (lines 3688-3704)
  • Proper handling of v2-specific fields: BlockHeight, DisableFlags
  • Clear error on unknown gossip versions (lines 3631-3635)

SQL Query (sqldb/sqlc/queries/graph.sql:957-968):

Staleness check is correctly implemented

WHERE (
    EXCLUDED.version = 1 AND (
        graph_channel_policies.last_update IS NULL
        OR EXCLUDED.last_update > graph_channel_policies.last_update  -- Strict ordering for v1
    )
)
OR (
    EXCLUDED.version = 2 AND (
        graph_channel_policies.block_height IS NULL
        OR EXCLUDED.block_height >= graph_channel_policies.block_height  -- Allow same-block for v2
    )
)

The use of >= for v2 (vs > for v1) allows multiple updates in the same block, which is appropriate since block height is less granular than timestamps.

KV Store (graph/db/kv_store.go)

Correctly rejects v2 operations

The KV store explicitly returns ErrVersionNotSupportedForKVDB for v2 operations (lines 59-66 in the diff context). This is the right approach for a v1-only backend.

Reject Cache (graph/db/reject_cache.go)

Version isolation is well-designed

type rejectCacheKey struct {
    version lnwire.GossipVersion
    chanID  uint64
}

This prevents cross-version cache collisions and allows independent tracking of v1 and v2 policies for the same channel. The separate timestamp fields (upd1Time/upd2Time for v1, upd1BlockHeight/upd2BlockHeight for v2) are correctly populated.

4. Integration Points

Discovery/Gossiper (discovery/gossiper.go)

Minimal changes, uses new constructor

The gossiper now uses models.ChanEdgePolicyFromWire (line 6 of diff), which is the correct abstraction point.

Note: The current implementation of IsStaleEdgePolicy in graph/builder.go:1357-1402 still only handles v1 timestamps. This is fine for this PR since v2 gossip is not yet active, but will need updating in future PRs.

Graph Builder (graph/builder.go)

Updated to use v1-specific methods

  • HasV1ChannelEdge used in staleness checks (line 1361)
  • ApplyChannelUpdate uses ChanEdgePolicyFromWire (line 955)

5. Test Coverage

Comprehensive test updates

  • New versioned tests: testEdgePolicyCRUD, testIncompleteChannelPolicies
  • Updated compareEdgePolicies to be version-aware
  • All existing tests updated with Version: lnwire.GossipVersion1

Issues & Recommendations

🟡 Minor Issues

  1. Typo in comment (lnwire/channel_update_2.go:185)
    "genisis" → "genesis"

  2. IsDisabled() semantic difference (graph/db/models/channel_edge_policy.go:161-171)
    The v2 behavior (both directions must be disabled) differs from v1. While documented, consider adding helper methods like IsIncomingDisabled() and IsOutgoingDisabled() for v2 to make directional queries explicit, as suggested in @bhandras's review comment.

  3. Documentation consistency (graph/db/graph.go:666,673,909,918)
    As noted by Gemini Code Assist, several exported methods have brief godoc comments. Consider expanding them to match the detail level in the Store interface definitions.

✅ Good Practices Observed

  • Clear separation of v1/v2 logic
  • Version-specific staleness semantics correctly implemented
  • Explicit error handling for unsupported versions
  • Comprehensive test coverage
  • Backward compatibility maintained (all v1 paths unchanged)

📋 Future Work (Not Blocking)

  1. Update IsStaleEdgePolicy in graph/builder.go to handle v2 block heights (will be needed when v2 gossip is enabled)
  2. Version the channel cache similar to the reject cache (as noted by @bhandras)
  3. Extend ChanUpdatesInHorizon to support v2 (currently hardcoded to v1 at line 1184 of sql_store.go)

Security Considerations

No security concerns identified

  • SQL queries use parameterized statements (no injection risks)
  • Version validation present before processing
  • Signature validation occurs at wire level (not changed by this PR)
  • No new authentication or authorization logic

Performance Considerations

Well-optimized

  • Reject cache remains bounded (random eviction when full)
  • Database indexes should handle version-aware queries efficiently
  • No N+1 query patterns introduced

Conclusion

This is a well-executed PR that successfully adds v2 gossip support to the database layer. The code is clean, well-tested, and maintains backward compatibility. The minor issues noted are mostly documentation and polish items that don't block the core functionality.

Recommendation: Approve with minor suggestions for follow-up improvements.


Branch: g175-db-6

Copy link
Copy Markdown
Collaborator

@bitromortac bitromortac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really great work here, LGTM 🎉

type ChannelEdgePolicy struct {
// Version is the gossip version of the channel update that produced
// this policy.
Version lnwire.GossipVersion
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this needs to be set as well in dev_server.go

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great catch! updated

Comment thread graph/db/models/channel_edge_policy.go Outdated
return &ChannelEdgePolicy{
Version: lnwire.GossipVersion2,
SigBytes: upd.Signature.Val.ToSignatureBytes(),
ChannelID: upd.ShortChannelID.Val.ToUint64(),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that comment is really confusing

Comment thread graph/db/graph_test.go
Comment on lines +2396 to 2398
edge2UpdateTime.Unix(), false,
)
edge2.ChannelFlags = 1
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like ChannelFlags can be deleted as it's set in newEdgePolicy, same below?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice - good catch - removed!

Comment thread graph/db/reject_cache.go Outdated
Comment on lines +56 to +57
upd1BlockHeight int64
upd2BlockHeight int64
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe could use uint32 because of the block heights?

Comment thread graph/db/graph_test.go
name: "edge insertion deletion",
test: testEdgeInsertionDeletion,
},
{
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the commit message mentions equalExtraSignedFields, which is not included here (handled by the deep equal in the comparison helper I think)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch! thanks. due to refactor yesterday. will fix!

Comment thread graph/db/graph_test.go
Comment on lines +1205 to +1206
edge1 := newEdgePolicy(v, chanID, nextUpdateTime().Unix(), true)
edge2 := newEdgePolicy(v, chanID, nextUpdateTime().Unix(), false)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: perhaps we can set the timestamps to zero as they are updated later?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes sure 👍

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm i think they are used once before being updated though right?

gonna leave for now

Extend ChannelEdgePolicy to support v2 channel updates by adding:

- Version field to track gossip protocol version (v1 or v2).
- LastBlockHeight for v2's block-height-based timestamps.
- SecondPeer flag to indicate which peer announced the policy in v2.
- DisableFlags for v2-specific channel disable signaling.
- ExtraSignedFields map for v2 extra signed TLV data.

Add version-aware methods:
- IsNode1() determines if the policy was announced by node_1, handling
  both v1 (via ChannelFlags direction bit) and v2 (via SecondPeer).
- IsDisabled() checks disable status using ChannelFlags for v1 and
  DisableFlags for v2.
- String() provides version-appropriate string representations.

The new fields use zero values for v1 compatibility (Version defaults
to GossipVersion1, LastBlockHeight to 0, SecondPeer to false). This
lays the groundwork for v2 policy support; a subsequent commit will
handle reading and writing these fields from/to the database.
Replace MessageFlags and ChannelFlags bitfields in CachedEdgePolicy
with explicit boolean fields to improve clarity and support both v1
and v2 channel updates:

- Replace MessageFlags with HasMaxHTLC boolean.
- Replace ChannelFlags with IsNode1 and IsDisabled booleans.
- Update NewCachedPolicy to extract these fields version-appropriately:
  - For v1: derive from MessageFlags and ChannelFlags bits.
  - For v2: derive from policy.SecondPeer and policy.DisableFlags.

Update all call sites that used method calls IsNode1() and IsDisabled()
to instead access the fields directly. This includes:
- graph_cache.go: policy direction and disable checks
- unified_edges.go: HasMaxHTLC and IsDisabled checks
- Tests: policy construction and assertions

This refactoring improves readability by making the cached policy's
state explicit rather than encoded in bitfields, and enables seamless
support for both gossip protocol versions.
Add ChanEdgePolicyFromWire to construct ChannelEdgePolicy from channel
update messages, centralizing v1/v2 field mapping.
Update call sites to use the helper:

- discovery/gossiper: handleChanUpdate
- graph/builder: ApplyChannelUpdate
- routing/router_test: ApplyChannelUpdate test helper

This consolidates update-to-policy conversion logic across versions.
Ensure policies loaded for graph cache reconstruction are tagged as v1.
Update buildChanPolicy and related functions in both KV and SQL stores
to properly construct ChannelEdgePolicy with version-specific fields:

KVStore changes:
- Reject non-v1 policies in updateEdgePolicy and serializeChanEdgePolicy
  since KV store only supports v1 gossip protocol.
- Set Version to GossipVersion1 when deserializing policies from KV.

SQLStore changes:
- Add isNode1 parameter to buildChanPolicy functions to properly set
  SecondPeer field (v2 uses SecondPeer instead of ChannelFlags direction).
- Extract Version from database and populate version-specific fields:
  - For v1: MessageFlags, ChannelFlags, LastUpdate, ExtraOpaqueData
  - For v2: DisableFlags, LastBlockHeight, ExtraSignedFields
- Thread isNode1 through buildChanPolicyWithBatchData and
  buildCachedChanPolicies call sites.

This enables the SQL store to read and reconstruct both v1 and v2
channel policies from the database with proper field mapping.
Make the reject cache version-aware so v1 and v2 policy state can be
cached independently per channel ID.  Add helpers to store v1 timestamps
or v2 block heights and thread the versioned cache key through KV/SQL
store cache accesses.
Add a new HasChannelEdge method that takes a gossip version parameter
and returns only existence and zombie status, without timestamp data.
This supports both v1 and v2 gossip protocols.

The original HasChannelEdge method is renamed to HasV1ChannelEdge to
preserve v1-specific functionality for callers that need timestamp
information. All call sites are updated accordingly.

The SQL store implementation now handles both gossip versions, using
timestamps for v1 and block heights for v2 policies, with proper
reject cache support for both versions.
Update ForEachChannel to accept a gossip version parameter, allowing
callers to specify which gossip version's channels should be iterated.
This change prepares the graph database for supporting multiple gossip
versions while maintaining backward compatibility.

The KVStore implementation validates that only GossipVersion1 is
requested, returning ErrVersionNotSupportedForKVDB for other versions.
The SQLStore implementation validates known versions and passes the
version through to the underlying paginated query.

Update VersionedGraph to include a ForEachChannel method that
automatically uses its configured gossip version, and update the
DescribeGraph RPC handler to use the v1Graph instead of the global
graphDB.
Update the UpsertChannelPolicy query to apply different staleness
checks based on gossip version. For v1 policies, continue checking
last_update timestamps. For v2 policies, check block_height instead,
using >= comparison to handle policies from the same block.

The version-specific WHERE clause ensures that policy updates are only
applied when they contain newer information according to the versioning
scheme appropriate for that gossip version.
Convert testEdgePolicyCRUD to a versioned test that runs against both
v1 and v2 gossip versions. Update the test to use version-specific
edge creation helpers and to test version-specific fields and flag
behavior (ChannelFlags/MessageFlags for v1, DisableFlags/
ExtraSignedFields for v2).
Update ForEachNodeChannel to accept a gossip version parameter,
allowing callers to specify which gossip version's channels should be
iterated. This change mirrors the approach taken in ForEachChannel and
prepares the graph database for supporting multiple gossip versions
while maintaining backward compatibility.

The Store interface is updated to include the version parameter, and
both KVStore and SQLStore implementations are updated accordingly:

- KVStore validates that only GossipVersion1 is requested, returning
  ErrVersionNotSupportedForKVDB for other versions.
- SQLStore passes the version through to the underlying node query,
  enabling version-specific channel iteration.

The ChannelGraph wrapper is updated to accept and pass through the
version parameter. VersionedGraph gains a ForEachNodeChannel method
that automatically uses its configured gossip version, providing a
clean interface for version-specific operations.

Update all call sites to explicitly pass lnwire.GossipVersion1, except
for the local channel manager in server.go which now uses the v1Graph
directly (matching the pattern used in other parts of the codebase).
@lightninglabs-deploy lightninglabs-deploy added the severity-critical Requires expert review - security/consensus critical label Feb 12, 2026
@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

🔴 PR Severity: CRITICAL

graph/db, lnwire, server coordination | 32 files | 1,727 lines changed

🔴 Critical (3 files)
  • lnwire/channel_update_2.go - Lightning wire protocol message handling for v2 gossip
  • server.go - Core server coordination changes
  • rpcserver.go - Core RPC server coordination
🟠 High (16 files)
  • discovery/gossiper.go - Gossip protocol modifications
  • graph/builder.go - Network graph builder changes
  • graph/db/graph.go - Network graph database core operations
  • graph/db/graph_cache.go - Graph caching layer
  • graph/db/interfaces.go - Database interface changes
  • graph/db/kv_store.go - Key-value store operations
  • graph/db/models/cached_edge_policy.go - Edge policy model changes
  • graph/db/models/channel_edge_policy.go - Channel edge policy model (96 lines added)
  • graph/db/reject_cache.go - Rejection cache logic (86 lines added)
  • graph/db/sql_store.go - SQL store operations (348 lines added)
  • routing/localchans/manager.go - Local channel management
  • routing/unified_edges.go - Unified edge handling
  • lnrpc/devrpc/dev_server.go - Developer RPC endpoints
  • sqldb/sqlc/graph.sql.go - Auto-generated SQL query code (138 lines added)
  • sqldb/sqlc/queries/graph.sql - SQL query definitions
  • netann/chan_status_manager_test.go - Test file
🟡 Medium (0 files)

None

🟢 Low (13 files)
  • docs/release-notes/release-notes-0.21.0.md - Release notes
  • autopilot/prefattach_test.go - Test file
  • graph/builder_test.go - Test file
  • graph/db/benchmark_test.go - Benchmark test
  • graph/db/graph_cache_test.go - Test file
  • graph/db/graph_test.go - Test file (197 lines added)
  • graph/db/reject_cache_test.go - Test file
  • graph/notifications_test.go - Test file
  • lnrpc/invoicesrpc/addinvoice_test.go - Test file
  • routing/localchans/manager_test.go - Test file
  • routing/pathfind_test.go - Test file
  • routing/router_test.go - Test file
  • routing/unified_edges_test.go - Test file

Analysis

This PR adds v2 gossip support for channel policies, which is a CRITICAL change due to:

  1. Wire Protocol Changes: Modifications to lnwire/channel_update_2.go affect the Lightning Network wire protocol, which requires expert review to ensure compatibility and correctness.

  2. Core Server Integration: Changes to server.go and rpcserver.go integrate the new v2 gossip functionality into the core server coordination logic.

  3. Extensive Graph Database Changes: The PR modifies the network graph database layer extensively, including:

    • New SQL queries and store operations (348 lines in sql_store.go)
    • Channel edge policy model updates (96 lines added)
    • Rejection cache logic (86 lines added)
    • Database interface changes
  4. Scale: 32 total files changed with 1,727 lines modified (excluding only documentation). The PR touches multiple critical subsystems: wire protocol, graph database, routing, gossip, and server coordination.

  5. Complexity: This is a foundational change enabling v2 gossip, which will affect how channel policies are propagated and stored throughout the Lightning Network.

Recommendation: This PR requires expert review from maintainers familiar with the Lightning Network wire protocol, graph database architecture, and gossip protocol implementation.


To override, add a severity-override-{critical,high,medium,low} label.

@ellemouton ellemouton merged commit 6c4617f into lightningnetwork:elle-g175Prep-base Feb 12, 2026
49 of 50 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in v0.21 Feb 12, 2026
@ellemouton ellemouton deleted the g175-db-6 branch February 12, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

severity-critical Requires expert review - security/consensus critical

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants