mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
195 lines
5.4 KiB
Go
195 lines
5.4 KiB
Go
//go:build dev
|
|
|
|
package migsets
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/golang-migrate/migrate/v4"
|
|
"github.com/golang-migrate/migrate/v4/database"
|
|
"github.com/lightninglabs/lightning-terminal/accounts"
|
|
"github.com/lightninglabs/lightning-terminal/db/sqlcmig6"
|
|
"github.com/lightninglabs/lightning-terminal/firewalldb"
|
|
"github.com/lightninglabs/lightning-terminal/session"
|
|
"github.com/lightningnetwork/lnd/clock"
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
"github.com/lightningnetwork/lnd/sqldb/v2"
|
|
)
|
|
|
|
// MakePostStepCallbacksMig6 turns the post migration checks into a map of post
|
|
// step callbacks that can be used with the migrate package. The keys of the map
|
|
// are the migration versions, and the values are the callbacks that will be
|
|
// executed after the migration with the corresponding version is applied.
|
|
func MakePostStepCallbacksMig6(ctx context.Context,
|
|
basicClient lnrpc.LightningClient, db *sqldb.BaseDB,
|
|
macPath string, clock clock.Clock,
|
|
migVersion uint) migrate.ProgrammaticMigrEntry {
|
|
|
|
mig6queries := sqlcmig6.NewForType(db, db.BackendType)
|
|
mig6executor := sqldb.NewTransactionExecutor(
|
|
db, func(tx *sql.Tx) *sqlcmig6.Queries {
|
|
return mig6queries.WithTx(tx)
|
|
},
|
|
)
|
|
|
|
pMigr := func(_ *migrate.Migration, _ database.Driver) error {
|
|
// We ignore the actual driver that's being returned here, since
|
|
// we use migrate.NewWithInstance() to create the migration
|
|
// instance from our already instantiated database backend that
|
|
// is also passed into this function.
|
|
return mig6executor.ExecTx(
|
|
ctx, sqldb.WriteTxOpt(),
|
|
func(q6 *sqlcmig6.Queries) error {
|
|
log.Infof("Running post migration callback "+
|
|
"for migration version %d", migVersion)
|
|
|
|
return kvdbToSqlMigrationCallback(
|
|
ctx, basicClient, macPath, db, clock,
|
|
q6,
|
|
)
|
|
}, sqldb.NoOpReset,
|
|
)
|
|
}
|
|
|
|
return migrate.ProgrammaticMigrEntry{
|
|
// We want the migration to rerun on next startup if it errors,
|
|
// and not set the user's db to a dirty state.
|
|
ResetVersionOnError: true,
|
|
ProgrammaticMigr: pMigr,
|
|
}
|
|
}
|
|
|
|
func kvdbToSqlMigrationCallback(ctx context.Context,
|
|
basicClient lnrpc.LightningClient, macPath string, _ *sqldb.BaseDB,
|
|
clock clock.Clock, q *sqlcmig6.Queries) error {
|
|
|
|
start := time.Now()
|
|
log.Infof("Starting KVDB to SQL migration for all stores")
|
|
|
|
accountStore, err := accounts.NewBoltStore(
|
|
filepath.Dir(macPath), accounts.DBFilename, clock,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func() {
|
|
err := accountStore.Close()
|
|
if err != nil {
|
|
log.Errorf("Error closing bbolt account store during "+
|
|
"migration: %v", err)
|
|
}
|
|
}()
|
|
|
|
err = accounts.MigrateAccountStoreToSQL(ctx, accountStore.DB, q)
|
|
if err != nil {
|
|
return fmt.Errorf("error migrating account store to "+
|
|
"SQL: %w", err)
|
|
}
|
|
|
|
sessionStore, err := session.NewDB(
|
|
filepath.Dir(macPath), session.DBFilename,
|
|
clock, accountStore,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func() {
|
|
err := sessionStore.Close()
|
|
if err != nil {
|
|
log.Errorf("Error closing bbolt session store during "+
|
|
"migration: %v", err)
|
|
}
|
|
}()
|
|
|
|
err = session.MigrateSessionStoreToSQL(ctx, sessionStore.DB, q)
|
|
if err != nil {
|
|
return fmt.Errorf("error migrating session store to "+
|
|
"SQL: %w", err)
|
|
}
|
|
|
|
firewallStore, err := firewalldb.NewBoltDB(
|
|
filepath.Dir(macPath), firewalldb.DBFilename,
|
|
sessionStore, accountStore, clock,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func() {
|
|
err := firewallStore.Close()
|
|
if err != nil {
|
|
log.Errorf("Error closing bbolt rules store during "+
|
|
"migration: %v", err)
|
|
}
|
|
}()
|
|
|
|
// We'll fetch the macaroonIDList from `lnd` next. Note that since lnd's
|
|
// RPC servers may not have been fully started yet if the execution of
|
|
// accounts and session migration were really quick, we poll the request
|
|
// up to 10 times with a 0.5 second delay between the attempts. This
|
|
// should be a sufficient amount of time for the RPC servers to start.
|
|
const (
|
|
maxListMacaroonIDAttempts = 10
|
|
listMacaroonIDRetryDelay = 500 * time.Millisecond
|
|
)
|
|
|
|
var macaroonIDList *lnrpc.ListMacaroonIDsResponse
|
|
for i := 1; i <= maxListMacaroonIDAttempts; i++ {
|
|
macaroonIDList, err = basicClient.ListMacaroonIDs(
|
|
ctx, &lnrpc.ListMacaroonIDsRequest{},
|
|
)
|
|
if err == nil {
|
|
break
|
|
}
|
|
|
|
if i == maxListMacaroonIDAttempts {
|
|
return fmt.Errorf("error listing macaroon IDs when "+
|
|
"migrating stores to SQL after %d attempts: %w",
|
|
maxListMacaroonIDAttempts, err)
|
|
}
|
|
|
|
log.Warnf("Failed to list macaroon IDs when migrating "+
|
|
"stores to SQL (attempt %d/%d), retrying in %v: %v",
|
|
i, maxListMacaroonIDAttempts, listMacaroonIDRetryDelay,
|
|
err)
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return fmt.Errorf("context canceled while retrying "+
|
|
"to list macaroon IDs: %w", ctx.Err())
|
|
case <-time.After(listMacaroonIDRetryDelay):
|
|
}
|
|
}
|
|
|
|
log.Infof("Successfully listed macaroon IDs during store migration.")
|
|
|
|
var macRootKeyIDs [][]byte
|
|
if macaroonIDList != nil {
|
|
for _, rootKeyID := range macaroonIDList.RootKeyIds {
|
|
rootKeyBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(rootKeyBytes[:], rootKeyID)
|
|
|
|
macRootKeyIDs = append(macRootKeyIDs, rootKeyBytes)
|
|
}
|
|
}
|
|
|
|
err = firewalldb.MigrateFirewallDBToSQL(
|
|
ctx, firewallStore.DB, q, macRootKeyIDs,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("error migrating firewalldb store "+
|
|
"to SQL: %w", err)
|
|
}
|
|
|
|
log.Infof("Succesfully migrated all KVDB stores to SQL in: %v",
|
|
time.Since(start))
|
|
|
|
return nil
|
|
}
|