What changed, and why it matters
This commit adds a new database-backed store that records which Bitcoin network (mainnet, testnet, etc.) the node was first started with. On every startup it checks the stored value against the current configuration and refuses to run if they differ. This is a defensive safety feature, not a vulnerability fix or exploitable change.
No security action required. Treat as normal feature/refactor commit. If reviewing the broader PR, verify that the SQL migration creating chain_params table and the call site integrating ValidateNetwork are also correct.
Security signals we found
New defensive validation preventing accidental network/database mismatch
Transaction-scoped read-after-write for consistent startup check
Input validation: nil chain parameters rejected
Evidence from the diff
The change introduces chainparams/store.go, implementing a SQL store that persists a normalized network name via InsertChainNetwork/GetChainNetwork and validates it on startup through ValidateNetwork. It uses sqldb transactions, handles nil chain params, and returns ErrNetworkMismatch on mismatch. There is no security defect visible in the diff; the code is a new guardrail.
Changed components
chainparams/store.gosqldbsqlc.QueriesInspect captured patch +114 / −0
diff --git a/chainparams/store.go b/chainparams/store.go
new file mode 100644
index 0000000..7459d51
--- /dev/null
+++ b/chainparams/store.go
@@ -0,0 +1,114 @@
+package chainparams
+
+import (
+ "context"
+ "database/sql"
+ "errors"
+ "fmt"
+
+ "github.com/btcsuite/btcd/chaincfg"
+ "github.com/lightningnetwork/lnd/lncfg"
+ "github.com/lightningnetwork/lnd/sqldb"
+ "github.com/lightningnetwork/lnd/sqldb/sqlc"
+)
+
+// ErrNetworkMismatch is returned by ValidateNetwork when the network stored in
+// the database does not match the network lnd is configured to use.
+var ErrNetworkMismatch = errors.New("database network mismatch")
+
+// SQLChainParamQueries defines the SQL queries required by Store.
+type SQLChainParamQueries interface {
+ InsertChainNetwork(ctx context.Context, network string) error
+ GetChainNetwork(ctx context.Context) (string, error)
+}
+
+// BatchedChainParamQueries is a version of SQLChainParamQueries that is
+// capable of batched database operations.
+type BatchedChainParamQueries interface {
+ SQLChainParamQueries
+
+ sqldb.BatchedTx[SQLChainParamQueries]
+}
+
+// Store is a database-backed store that persists and retrieves chain-level
+// parameters such as the network the database was initialised for.
+type Store struct {
+ db BatchedChainParamQueries
+}
+
+// NewStore creates a new chain params Store backed by the given BaseDB.
+func NewStore(db *sqldb.BaseDB) *Store {
+ executor := sqldb.NewTransactionExecutor(
+ db, func(tx *sql.Tx) SQLChainParamQueries {
+ return db.WithTx(tx)
+ },
+ )
+
+ return &Store{db: executor}
+}
+
+// ValidateNetwork checks that the network stored in the chain_params table
+// matches the provided network. On the first call the network is persisted so
+// that subsequent restarts can detect an accidental network switch.
+func (s *Store) ValidateNetwork(ctx context.Context,
+ net *chaincfg.Params) error {
+
+ network, err := normalizeNetworkName(net)
+ if err != nil {
+ return err
+ }
+
+ return s.db.ExecTx(
+ ctx, sqldb.WriteTxOpt(),
+ func(tx SQLChainParamQueries) error {
+ // 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)
+ if err != nil {
+ return fmt.Errorf("unable to set network in "+
+ "chain_params: %w", err)
+ }
+
+ // Read back whatever is stored. This is either the
+ // value we just inserted (first startup) or a value
+ // from a previous run.
+ storedNetwork, err := tx.GetChainNetwork(ctx)
+ if err != nil {
+ return fmt.Errorf("unable to read network "+
+ "from chain_params: %w", err)
+ }
+
+ if storedNetwork != network {
+ return fmt.Errorf("%w: the database was "+
+ "previously used with network '%s', "+
+ "but lnd is now configured for "+
+ "network '%s'. To fix this, either "+
+ "point lnd at a different database "+
+ "or reconfigure lnd to use "+
+ "network '%s'", ErrNetworkMismatch,
+ storedNetwork, network, storedNetwork)
+ }
+
+ return nil
+ }, sqldb.NoOpReset,
+ )
+}
+
+// normalizeNetworkName returns the stable network identifier persisted in the
+// chain_params table.
+func normalizeNetworkName(net *chaincfg.Params) (string, error) {
+ if net == nil {
+ return "", fmt.Errorf("chain parameters must not be nil")
+ }
+
+ network := lncfg.NormalizeNetwork(net.Name)
+ if network == "" {
+ return "", fmt.Errorf("chain parameters must define a network")
+ }
+
+ return network, nil
+}
+
+// Compile-time check that *sqlc.Queries implements SQLChainParamQueries.
+var _ SQLChainParamQueries = (*sqlc.Queries)(nil)
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.