Skip to content

chanstate: introduce Store interface (PR 1 of channel-state decomposition)#10777

Open
ziggie1984 wants to merge 4 commits intolightningnetwork:masterfrom
ziggie1984:chanstate-sql-migration-pr1-interface
Open

chanstate: introduce Store interface (PR 1 of channel-state decomposition)#10777
ziggie1984 wants to merge 4 commits intolightningnetwork:masterfrom
ziggie1984:chanstate-sql-migration-pr1-interface

Conversation

@ziggie1984
Copy link
Copy Markdown
Collaborator

@ziggie1984 ziggie1984 commented Apr 28, 2026

Summary

First PR in the channel-state decomposition series. Introduces the
chanstate.Store interface as the seam between the channel-state subsystem
and its consumers, mirroring the pattern in invoices/, payments/db/, and
graph/db/. No consumer migrates, no data moves, no behavior changes.

Store is composed of two embedded sub-interfaces — ChannelStore for core
channel-state operations and InitialForwardingPolicyStore for the
per-channel forwarding policy chosen at channel-open time — so future
decomposition PRs can extend the contract without inflating a single
monolithic interface.

Four commits:

  1. channeldb: drop unused kvdb.RTx parameter from FetchChannelByID
    prerequisite so the interface stays free of kvdb types. All four call
    sites already pass nil.
  2. chanstate: introduce Store interface — new package with Store and its
    ChannelStore sub-interface, a package logger, and the compile-time
    assertion var _ Store = (*channeldb.ChannelStateDB)(nil) that guards
    against signature drift. GetParentDB (test-only) and LinkNodeDB
    (separate domain) are intentionally excluded.
  3. chanstate: add InitialForwardingPolicyStore sub-interface — adds the
    second sub-interface for the initial-forwarding-policy methods, embedded
    into Store.
  4. docs: release note for chanstate Store interface — seeds the v0.22
    "Native SQL migration of the channel state and switch data" series so
    follow-up PRs (consumer swap, KV/SQL stores, revocation log, forwarding
    package, etc.) hang off the same heading.

Test plan

  • go build ./... clean
  • Compile-time assertion catches any interface ↔ concrete drift
  • make unit pkg=channeldb and make lint pass

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, 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 initiates the decomposition of the channel-state management logic into a dedicated subsystem. By introducing the ChannelStore interface and implementing a deferred cleanup mechanism for bulk historical data, the changes prepare the codebase for future SQL-backed storage implementations while maintaining current performance and consistency guarantees.

Highlights

  • ChannelStore Interface: Introduced a new chanstate/ChannelStore interface to decouple the channel-state subsystem from the concrete channeldb.ChannelStateDB implementation, facilitating future SQL-backed storage.
  • Deferred Cleanup Mechanism: Implemented a two-phase channel closure process for SQL-backed backends, where bulk historical data deletion is deferred to startup to keep the synchronous close path performant.
  • API Cleanup: Removed the unused kvdb.RTx parameter from FetchChannelByID across all call sites to ensure the interface remains clean and free of database-specific types.

🧠 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.

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.

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.

@ziggie1984 ziggie1984 self-assigned this Apr 28, 2026
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 pull request introduces a deferred cleanup mechanism for closed channels on KV-over-SQL backends (SQLite and Postgres) to prevent long write-lock stalls during synchronous channel closure. The implementation splits the closure into two phases: Phase 1 atomically marks the channel as closed and removes it from open views, while Phase 2 defers the deletion of bulky historical data (revocation logs and forwarding packages) to the next startup. The PR also introduces a ChannelStore interface to abstract the persistence layer. Feedback focuses on improving the robustness of the cleanup queue by handling malformed records and ensuring that individual purge failures do not block the entire node startup process, as well as minor code simplifications.

Comment thread channeldb/db.go Outdated
Comment on lines +1984 to +2011
return cleanupBkt.ForEach(func(k, v []byte) error {
var op wire.OutPoint
if err := graphdb.ReadOutpoint(
bytes.NewReader(k), &op,
); err != nil {
log.Warnf("Skipping pending closed-channel "+
"cleanup with malformed "+
"chan_key=%x: %v", k, err)

return nil
}

rec, err := decodePendingCleanupRecord(v)
if err != nil {
log.Warnf("Skipping pending closed-channel "+
"cleanup for %v: unable to decode "+
"record: %v", op, err)

return nil
}

entries = append(entries, PendingCleanup{
ChanPoint: op,
Record: rec,
})

return nil
})
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

Malformed cleanup records are skipped and logged but remain in the pendingChanCleanupBucket. This results in persistent log noise on every startup. Consider collecting the keys of malformed records and deleting them in a separate transaction to keep the cleanup queue healthy.

Comment thread channeldb/db.go Outdated
Comment thread channeldb/db.go Outdated
@saubyk saubyk added this to lnd v0.22 Apr 28, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in lnd v0.22 Apr 28, 2026
@saubyk saubyk added this to the v0.22.0 milestone Apr 28, 2026
@saubyk saubyk moved this from Backlog to In progress in lnd v0.22 Apr 28, 2026
@ziggie1984 ziggie1984 force-pushed the chanstate-sql-migration-pr1-interface branch from 577cb0e to a91f13f Compare May 4, 2026 22:06
@github-actions github-actions Bot added the severity-critical Requires expert review - security/consensus critical label May 4, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

🔴 PR Severity: CRITICAL

Auto-classified | 4 files | 195 lines changed

🔴 Critical (2 files)
  • channeldb/db.go - Channel state persistence (channeldb/*)
  • server.go - Core server coordination
🟡 Medium (2 files)
  • chanstate/interface.go - New unclassified package (chanstate/*)
  • chanstate/log.go - New unclassified package (chanstate/*)

Analysis

This PR is classified CRITICAL due to changes in two independently critical areas:

  1. channeldb/db.go — The channeldb package manages channel state persistence and database operations. Any modification here can affect how channel state is read, written, or migrated, making it a high-risk area requiring expert review.

  2. server.go — The core server coordination file is explicitly listed as a critical path. Changes here can affect the overall node lifecycle and component wiring.

The new chanstate package (156 lines added) introduces a new interface/abstraction for channel state. While it doesn't directly map to an existing critical package and is classified MEDIUM on its own, it is being integrated via channeldb/db.go and server.go, both of which are critical touchpoints. Reviewers should pay close attention to how this new abstraction interacts with the existing channel state machinery.

No severity bump triggers were met (4 non-test files, 195 lines changed).


To override, add a severity-override-{critical,high,medium,low} label.
<!-- pr-severity-bot -->

@ziggie1984 ziggie1984 changed the base branch from master to elle-base-branch-channelstate-native-sql May 4, 2026 22:20
All four call sites pass nil for tx today (server.go, two in
channeldb/db_test.go, funding/manager_test.go). The internal
channelScanner(nil, selector) call inside FetchChannelByID is preserved
verbatim, so runtime behavior is unchanged.

This is a prerequisite for the upcoming chanstate.ChannelStore
interface: keeping the parameter would leak kvdb into a domain
interface.
@ziggie1984 ziggie1984 force-pushed the chanstate-sql-migration-pr1-interface branch from a91f13f to 9c66646 Compare May 4, 2026 22:36
@ziggie1984
Copy link
Copy Markdown
Collaborator Author

/gemini review

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 pull request introduces the ChannelStore interface within a new chanstate package, serving as a persistence contract to decouple the channel-state subsystem from the concrete channeldb implementation. As part of this refactoring, the FetchChannelByID method signature was simplified by removing the optional database transaction parameter, with corresponding updates made across tests and the main server logic. Regarding the review feedback, a style guide violation was identified in chanstate/log.go where the init function requires a comment explaining its purpose.

Comment thread chanstate/log.go
Comment on lines +15 to +17
func init() {
UseLogger(build.NewSubLogger("CHST", nil))
}
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

According to the LND Style Guide (line 17), every function must have a comment explaining its purpose. The init function is missing such a comment.

Suggested change
func init() {
UseLogger(build.NewSubLogger("CHST", nil))
}
// init initializes the package-global logger instance.
func init() {
UseLogger(build.NewSubLogger("CHST", nil))
}
References
  1. Every function must be commented with its purpose and assumptions. (link)

ziggie1984 added 3 commits May 5, 2026 11:49
Add a new chanstate package containing the Store interface plus a
package logger. The interface mirrors the public surface of
*channeldb.ChannelStateDB so the compile-time assertion

    var _ ChannelStore = (*channeldb.ChannelStateDB)(nil)

No consumer migrates in this commit.
Introduce InitialForwardingPolicyStore as a separate sub-interface
covering the per-channel initial forwarding policy that the funding
manager records at channel-open time and the link consumes on
activation. The sub-interface is embedded into Store so the existing
compile-time assertion

    var _ Store = (*channeldb.ChannelStateDB)(nil)

continues to enforce that *channeldb.ChannelStateDB satisfies the full
contract.

No consumer migrates in this commit.
Seeds the v0.22 "Native SQL migration of the channel state and switch
data" series under the Database header so subsequent decomposition PRs
(consumer migration, KV/SQL stores, revocation log, forwarding package,
etc.) can hang off the same heading as nested bullets, mirroring the
v0.21 payments series shape.
@ziggie1984 ziggie1984 force-pushed the chanstate-sql-migration-pr1-interface branch from 9c66646 to 299e8a8 Compare May 5, 2026 14:59
@github-actions github-actions Bot added severity-critical Requires expert review - security/consensus critical and removed severity-critical Requires expert review - security/consensus critical labels May 5, 2026
@ziggie1984 ziggie1984 changed the title chanstate: introduce ChannelStore interface (PR 1 of channel-state decomposition) chanstate: introduce Store interface (PR 1 of channel-state decomposition) May 5, 2026
ziggie1984 added a commit to ziggie1984/lnd that referenced this pull request May 5, 2026
Add a sibling bullet under the existing "Native SQL migration of the
channel state and switch data" heading, covering the consumer-side
swap that pairs with the interface introduced in lightningnetwork#10777.
ziggie1984 added a commit to ziggie1984/lnd that referenced this pull request May 5, 2026
Add a sibling bullet under the existing "Native SQL migration of the
channel state and switch data" heading, covering the consumer-side
swap that pairs with the interface introduced in lightningnetwork#10777.
@ziggie1984 ziggie1984 changed the base branch from elle-base-branch-channelstate-native-sql to master May 6, 2026 16:12
@saubyk saubyk moved this from In progress to In review in lnd v0.22 May 7, 2026
Copy link
Copy Markdown
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

Straight forward diff. Main comment is that I think we can avoid the mega channel store interface in favor of more domain specific interfaces.

Comment thread chanstate/interface.go
// ChannelStore is the persistence contract for core channel-state operations.
//
//nolint:interfacebloat
type ChannelStore interface {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Perhaps we can split this up into some more domain specific types, I can see the following splits here:

  • Open channels
  • Closed channels
  • Channel close mutations
  • Link nodes
  • Alias management
  • SCB

Then that way all the relevant call sites can take in slimmer interfaces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactoring severity-critical Requires expert review - security/consensus critical sql

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

3 participants