Skip to content

sqldb: add network-mismatch safeguard for native-SQL backends#10684

Merged
ziggie1984 merged 7 commits intolightningnetwork:masterfrom
ziggie1984:postgres-network-separation
Apr 10, 2026
Merged

sqldb: add network-mismatch safeguard for native-SQL backends#10684
ziggie1984 merged 7 commits intolightningnetwork:masterfrom
ziggie1984:postgres-network-separation

Conversation

@ziggie1984
Copy link
Copy Markdown
Collaborator

@ziggie1984 ziggie1984 commented Mar 27, 2026

This PR prevents silent data corruption that can occur when a user accidentally points lnd at a native-SQL database that was initialised for a different Bitcoin network.

Changes

A new metadata table (migration 000014) stores a single network key on first startup. On every subsequent restart, lnd reads that value and refuses to start if it doesn't match the configured network, printing a clear error message with remediation steps.

The check is implemented on BaseDB and applies to both the PostgreSQL and SQLite backends. The itest exercises the full startup-refusal path for PostgreSQL. Unit tests covering the mismatch and idempotent-match cases run against both backends via the test_db_postgres || test_db_sqlite build tag.

Fixes: #10354

@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch from d58fddd to 84fa870 Compare March 27, 2026 21:32
@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 introduces a safety mechanism to ensure that a database initialized for one Bitcoin network cannot be accidentally reused for another. By persisting the network identifier in a new metadata table during the first startup, the system can now detect and block incompatible configurations, providing clear error messages to the user instead of allowing silent data corruption.

Highlights

  • Network Mismatch Safeguard: Introduced a new metadata table to persist the Bitcoin network upon first database initialization, preventing silent data corruption from cross-network database reuse.
  • Validation Logic: Implemented a mandatory network validation check in the database startup sequence for both PostgreSQL and SQLite backends.
  • Testing: Added comprehensive unit tests for network validation and a new integration test specifically exercising the startup-refusal path for PostgreSQL.

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

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 mechanism to prevent silent data corruption by ensuring that the database is not reused across different Bitcoin networks. It adds a metadata table to store the network name on first startup and validates it on subsequent restarts for both PostgreSQL and SQLite backends. Feedback was provided regarding the accuracy of a comment in config_builder.go and a suggestion to improve code organization by moving the BaseDB.ValidateNetwork implementation to interfaces.go.

Comment thread config_builder.go Outdated
Comment thread sqldb/postgres.go Outdated
@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch from 84fa870 to 752ad80 Compare March 27, 2026 21:44
@saubyk saubyk added this to v0.21 Mar 27, 2026
@saubyk saubyk moved this to In progress in v0.21 Mar 27, 2026
@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch 2 times, most recently from 309ee14 to 3e33be4 Compare March 28, 2026 10:06
Comment thread sqldb/sqlc/migrations/000014_metadata.up.sql Outdated
Comment thread sqldb/interfaces.go Outdated
Comment thread sqldb/migrations.go Outdated
Comment thread sqldb/postgres.go Outdated
@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch 4 times, most recently from 3041b52 to 02ec43f Compare March 31, 2026 09:45
@ziggie1984 ziggie1984 requested a review from Roasbeef March 31, 2026 09:46
@gijswijs gijswijs self-requested a review March 31, 2026 09:50
@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch 3 times, most recently from b927793 to 5b917c4 Compare April 1, 2026 06:43
@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

@Roasbeef: review reminder
@ziggie1984, remember to re-request review from reviewers when ready

Copy link
Copy Markdown
Contributor

@Abdulkbk Abdulkbk left a comment

Choose a reason for hiding this comment

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

LGTM ⚡️

Comment thread itest/lnd_postgres_network_separation_test.go
// networks) is not covered here because the itest harness does not provide a
// direct path to inject an existing SQLite file into a new node. The feature
// is exercised for SQLite at the unit-test level in
// sqldb/validate_network_test.go.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

do you mean in chainparams/store_test.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.

updated !

Comment thread chainparams/store.go Outdated
func (s *Store) ValidateNetwork(ctx context.Context,
net *chaincfg.Params) error {

network := net.Name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

worth checking Name isn't empty here.

Also should we use network := lncfg.NormalizeNetwork(net.Name) to collapse testnet3 to testnet?

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.

^ sgtm

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.

updated

Comment thread chainparams/store.go
}

if storedNetwork != network {
return fmt.Errorf("%w: the database was "+
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the convention is to place the wrapped-error at the end, but I guess since ErrNetworkMismatch is produced here, it is fine.

@saubyk saubyk moved this from In progress to In review in v0.21 Apr 8, 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.

LGTM 🥬

Comment thread sqldb/sqlc/migrations/000015_chain_params.up.sql
Comment thread chainparams/store.go Outdated
func (s *Store) ValidateNetwork(ctx context.Context,
net *chaincfg.Params) error {

network := net.Name
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.

^ sgtm

Comment thread chainparams/store.go
// Insert the network only if the chain_params table is
// still empty. This is a no-op on every startup after
// the first.
err := tx.InsertChainNetwork(ctx, network)
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.

Alternatively we could let the foreign key error bubble up, then bail instead of the noop insert then check.

Non-blocking.

@saubyk saubyk added this to the v0.21.0 milestone Apr 9, 2026
@saubyk saubyk added sql postgres database Related to the database/storage of LND labels Apr 9, 2026
@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch from 5b917c4 to 847fdff Compare April 9, 2026 10:12
@ziggie1984
Copy link
Copy Markdown
Collaborator Author

Current network binding uses the normalized network name. This protects the standard networks and avoids cross-network reuse in the common case. It does not yet distinguish distinct custom signet networks that share the same signet name; strengthening chain identity for that case is left as follow-up work.

@github-actions github-actions Bot added the severity-critical Requires expert review - security/consensus critical label Apr 9, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 9, 2026

PR Severity: CRITICAL

Automated classification | 11 files (excl. tests/generated) | ~220 lines changed

Critical (6 files):

  • sqldb/migrations.go - registers new migration; sqldb/* database migrations are always CRITICAL
  • sqldb/sqlc/migrations/000014_chain_params.up.sql - new SQL migration adding chain_params table
  • sqldb/sqlc/migrations/000014_chain_params.down.sql - corresponding rollback migration
  • sqldb/sqlc/models.go - new ChainParam model added to sqldb schema
  • sqldb/sqlc/querier.go - new query interface entries for chain params
  • sqldb/sqlc/queries/chain_params.sql - raw SQL queries for chain params persistence

Medium (4 files):

  • chainparams/store.go - new chain params store abstraction (uncategorized package)
  • chainparams/test_postgres.go - test helper for Postgres backend (not a _test.go file)
  • chainparams/test_sqlite.go - test helper for SQLite backend (not a _test.go file)
  • config_builder.go - wires the new chain params store during node initialization

Low (1 file):

  • docs/release-notes/release-notes-0.21.0.md - release note entry for this feature

Analysis:

This PR introduces a new SQL-backed chain_params store together with migration 000014, which creates a new database table. Per classification rules, all sqldb/* changes and especially any new migrations are automatically CRITICAL. The migration permanently alters the database schema, so it requires careful expert review to ensure:

  1. The migration is idempotent and the rollback (.down.sql) is correct.
  2. The new chain_params table interacts safely with existing tables under concurrent access.
  3. sqldb/migrations.go registers the migration at the right sequence number with no gaps or conflicts.
  4. config_builder.go initialises the new store without introducing a startup regression.

No severity bump was applied: 11 non-excluded files (< 20) and ~220 lines changed (< 500), with a single distinct critical package (sqldb).

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

@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 a mechanism to prevent silent data corruption by persisting and validating the Bitcoin network type within the SQL database. It adds a new chain_params table and a Store to manage network validation during startup for both Postgres and SQLite backends. A logic error was identified in config_builder.go where the network validation check could be bypassed if the migration skip flag for the inactive database backend was set, potentially leaving the safeguard disabled in certain configurations.

Comment thread config_builder.go Outdated
@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch from 847fdff to 4066315 Compare April 9, 2026 20:36
@ziggie1984
Copy link
Copy Markdown
Collaborator Author

needs to be merged after #10726

@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch from 4066315 to b3f35b6 Compare April 9, 2026 21:10
@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch from b3f35b6 to def2f24 Compare April 10, 2026 06:13
Copy link
Copy Markdown
Collaborator

@gijswijs gijswijs left a comment

Choose a reason for hiding this comment

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

Apart from some nits, this thing LGTM!

Please fix the typo in the penultimate commit: ("release not" → "release note")

🎉

Comment thread sqldb/sqlc/migrations/000015_chain_params.up.sql
Comment thread chainparams/store_test.go
Comment thread itest/lnd_postgres_network_separation_test.go
Comment thread lntest/harness.go
Comment thread config_builder.go
Comment thread config_builder.go Outdated
This is in particular important when running with a postgres
backend.

This only works if you run LND with the native sql flag but
people should run it with this flag from 21 on anyways.
Skip the chain_params network check when startup is explicitly
configured to skip SQL migrations. In that mode the schema is assumed
to already be managed externally, and the chain_params table may not
exist yet. Avoid failing startup on a missing table in this path.
@ziggie1984 ziggie1984 force-pushed the postgres-network-separation branch from def2f24 to e744e19 Compare April 10, 2026 10:04
@ziggie1984 ziggie1984 enabled auto-merge April 10, 2026 10:07
@ziggie1984 ziggie1984 merged commit 0a2f486 into lightningnetwork:master Apr 10, 2026
37 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in v0.21 Apr 10, 2026
@ziggie1984 ziggie1984 deleted the postgres-network-separation branch April 10, 2026 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

database Related to the database/storage of LND postgres severity-critical Requires expert review - security/consensus critical sql

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[feature]: Network Separation Issue for PostgreSQL Backend

6 participants