Skip to content

Commit 6a54d3b

Browse files
committed
sqldb: add network validation interface and backends
1 parent 383e058 commit 6a54d3b

4 files changed

Lines changed: 88 additions & 0 deletions

File tree

sqldb/interfaces.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,12 @@ type DB interface {
401401
// both sqlc and custom in-code migrations.
402402
ApplyAllMigrations(ctx context.Context,
403403
customMigrations []MigrationConfig) error
404+
405+
// ValidateNetwork checks that the database was initialised for the
406+
// given Bitcoin network and returns an error if there is a mismatch.
407+
// On the very first call the network is recorded so that subsequent
408+
// calls can detect a change.
409+
ValidateNetwork(ctx context.Context, network string) error
404410
}
405411

406412
// BaseDB is the base database struct that each implementation can embed to

sqldb/no_sqlite.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,11 @@ func (s *SqliteStore) ApplyAllMigrations(context.Context,
3838

3939
return fmt.Errorf("SQLite backend not supported in WebAssembly")
4040
}
41+
42+
// ValidateNetwork is a no-op for the no-sqlite build; this backend is not
43+
// usable and always returns errors on initialization.
44+
//
45+
// NOTE: This is part of the sqldb.DB interface.
46+
func (s *SqliteStore) ValidateNetwork(context.Context, string) error {
47+
return nil
48+
}

sqldb/postgres.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,69 @@ func (s *PostgresStore) ExecuteMigrations(target MigrationTarget) error {
186186
)
187187
}
188188

189+
// metadataNetworkKey is the key used to store the network name in the metadata
190+
// table.
191+
const metadataNetworkKey = "network"
192+
193+
// ValidateNetwork checks that the network stored in the metadata table matches
194+
// the provided network name. On the first call the network is persisted so
195+
// that subsequent restarts can detect an accidental network switch.
196+
//
197+
// This prevents silent data corruption when a user points the same postgres
198+
// DSN at a different Bitcoin network.
199+
//
200+
// NOTE: This is part of the sqldb.DB interface.
201+
func (s *PostgresStore) ValidateNetwork(ctx context.Context,
202+
network string) error {
203+
204+
return s.BaseDB.ValidateNetwork(ctx, network)
205+
}
206+
207+
// ValidateNetwork checks that the network stored in the metadata table matches
208+
// the provided network name. On the first call the network is persisted so
209+
// that subsequent restarts can detect an accidental network switch.
210+
func (b *BaseDB) ValidateNetwork(ctx context.Context, network string) error {
211+
executor := NewTransactionExecutor(
212+
b, func(tx *sql.Tx) *sqlc.Queries {
213+
return b.WithTx(tx)
214+
},
215+
)
216+
217+
return executor.ExecTx(ctx, WriteTxOpt(), func(tx *sqlc.Queries) error {
218+
// Insert the network only if the key doesn't exist yet.
219+
// ON CONFLICT DO NOTHING means this is a no-op on every
220+
// startup after the first.
221+
err := tx.SetMetaValue(ctx, sqlc.SetMetaValueParams{
222+
Key: metadataNetworkKey,
223+
Value: network,
224+
})
225+
if err != nil {
226+
return fmt.Errorf("unable to set network in "+
227+
"metadata: %w", err)
228+
}
229+
230+
// Read back whatever is stored. This is either the value we
231+
// just inserted (first startup) or a value from a previous run.
232+
storedNetwork, err := tx.GetMetaValue(ctx, metadataNetworkKey)
233+
if err != nil {
234+
return fmt.Errorf("unable to read network from "+
235+
"metadata: %w", err)
236+
}
237+
238+
if storedNetwork != network {
239+
return fmt.Errorf("database network mismatch: the "+
240+
"database was previously used with network "+
241+
"'%s', but lnd is now configured for network "+
242+
"'%s'. To fix this, either point lnd at a "+
243+
"different database or reconfigure lnd to use "+
244+
"network '%s'",
245+
storedNetwork, network, storedNetwork)
246+
}
247+
248+
return nil
249+
}, func() {})
250+
}
251+
189252
// GetSchemaVersion returns the current schema version of the Postgres database.
190253
func (s *PostgresStore) GetSchemaVersion() (int, bool, error) {
191254
driver, err := pgx_migrate.WithInstance(s.DB, &pgx_migrate.Config{})

sqldb/sqlite.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,17 @@ func (s *SqliteStore) GetBaseDB() *BaseDB {
158158
return s.BaseDB
159159
}
160160

161+
// ValidateNetwork checks that the network stored in the metadata table matches
162+
// the provided network name. On the first call the network is persisted so
163+
// that subsequent restarts can detect an accidental network switch.
164+
//
165+
// NOTE: This is part of the sqldb.DB interface.
166+
func (s *SqliteStore) ValidateNetwork(ctx context.Context,
167+
network string) error {
168+
169+
return s.BaseDB.ValidateNetwork(ctx, network)
170+
}
171+
161172
// ApplyAllMigrations applies both the SQLC and custom in-code migrations to the
162173
// SQLite database.
163174
func (s *SqliteStore) ApplyAllMigrations(ctx context.Context,

0 commit comments

Comments
 (0)