mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Update LND, Aperture, and Taproot Assets to revisions using the btcd v2 modules, and update lndclient to v0.21.0-3. Migrate Loop chain, transaction, and address types to their corresponding v2 packages. The lndclient release includes the migration from: https://github.com/lightninglabs/lndclient/pull/280 Taproot Assets is temporarily replaced with its btcd v2 revision because the v0.8 release branch has not adopted the new modules. This raises the minimum Go version to 1.26 and changes exported address types.
164 lines
4.7 KiB
Go
164 lines
4.7 KiB
Go
package loopdb
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/v2"
|
|
postgres_migrate "github.com/golang-migrate/migrate/v4/database/postgres"
|
|
_ "github.com/golang-migrate/migrate/v4/source/file"
|
|
"github.com/lightninglabs/loop/loopdb/sqlc"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
dsnTemplate = "postgres://%v:%v@%v:%d/%v?sslmode=%v"
|
|
)
|
|
|
|
var (
|
|
// DefaultPostgresFixtureLifetime is the default maximum time a Postgres
|
|
// test fixture is being kept alive. After that time the docker
|
|
// container will be terminated forcefully, even if the tests aren't
|
|
// fully executed yet. So this time needs to be chosen correctly to be
|
|
// longer than the longest expected individual test run time.
|
|
DefaultPostgresFixtureLifetime = 10 * time.Minute
|
|
)
|
|
|
|
// PostgresConfig holds the postgres database configuration.
|
|
type PostgresConfig struct {
|
|
SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."`
|
|
Host string `long:"host" description:"Database server hostname."`
|
|
Port int `long:"port" description:"Database server port."`
|
|
User string `long:"user" description:"Database user."`
|
|
Password string `long:"password" description:"Database user's password."` //nolint:gosec
|
|
DBName string `long:"dbname" description:"Database name to use."`
|
|
MaxOpenConnections int32 `long:"maxconnections" description:"Max open connections to keep alive to the database server."`
|
|
RequireSSL bool `long:"requiressl" description:"Whether to require using SSL (mode: require) when connecting to the server."`
|
|
}
|
|
|
|
// DSN returns the data source name used to connect to the database.
|
|
func (s *PostgresConfig) DSN(hidePassword bool) string {
|
|
var sslMode = "disable"
|
|
if s.RequireSSL {
|
|
sslMode = "require"
|
|
}
|
|
|
|
password := s.Password
|
|
if hidePassword {
|
|
// Placeholder used for logging the DSN safely.
|
|
password = "****"
|
|
}
|
|
|
|
return fmt.Sprintf(dsnTemplate, s.User, password, s.Host, s.Port,
|
|
s.DBName, sslMode)
|
|
}
|
|
|
|
// PostgresStore is a database store implementation that uses a Postgres
|
|
// backend.
|
|
type PostgresStore struct {
|
|
*BaseDB
|
|
|
|
cfg *PostgresConfig
|
|
}
|
|
|
|
// In migration of sweeps table from outpoint_txid and outpoint_index to
|
|
// outpoint we need to reverse the order of bytes in outpoint_txid and to
|
|
// convert it to hex. This is done differently in sqlite and postgres.
|
|
//
|
|
// Changes from sqlite to postgres:
|
|
// - substr(blob, ...) -> get_byte(blob, index)
|
|
// - group_concat -> string_agg
|
|
// - 1-based indexing (32+1-i) -> 0-based (32 - i)
|
|
// - to_hex() + lpad(..., 2, '0') ensures each byte is two-digit hex
|
|
const (
|
|
txidSqlite = "group_concat(hex(substr(outpoint_txid,32+1-i,1)),'')"
|
|
txidPostgres = "string_agg(lpad(to_hex(get_byte(outpoint_txid, " +
|
|
"32 - i)), 2, '0'), '')"
|
|
)
|
|
|
|
// NewPostgresStore creates a new store that is backed by a Postgres database
|
|
// backend.
|
|
func NewPostgresStore(cfg *PostgresConfig,
|
|
network *chaincfg.Params) (*PostgresStore, error) {
|
|
|
|
log.Infof("Using SQL database '%s'", cfg.DSN(true))
|
|
|
|
rawDb, err := sql.Open("pgx", cfg.DSN(false))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !cfg.SkipMigrations {
|
|
// Now that the database is open, populate the database with
|
|
// our set of schemas based on our embedded in-memory file
|
|
// system.
|
|
//
|
|
// First, we'll need to open up a new migration instance for
|
|
// our current target database: sqlite.
|
|
driver, err := postgres_migrate.WithInstance(
|
|
rawDb, &postgres_migrate.Config{},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
postgresFS := newReplacerFS(sqlSchemas, map[string]string{
|
|
"BLOB": "BYTEA",
|
|
"INTEGER PRIMARY KEY": "SERIAL PRIMARY KEY",
|
|
txidSqlite: txidPostgres,
|
|
})
|
|
|
|
err = applyMigrations(
|
|
postgresFS, driver, "sqlc/migrations", cfg.DBName,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
queries := sqlc.New(rawDb)
|
|
|
|
baseDB := &BaseDB{
|
|
DB: rawDb,
|
|
Queries: queries,
|
|
network: network,
|
|
}
|
|
|
|
// Fix faulty timestamps in the database.
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
|
|
err = baseDB.FixFaultyTimestamps(ctx)
|
|
if err != nil {
|
|
log.Errorf("Failed to fix faulty timestamps: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
return &PostgresStore{
|
|
cfg: cfg,
|
|
BaseDB: baseDB,
|
|
}, nil
|
|
}
|
|
|
|
// NewTestPostgresDB is a helper function that creates a Postgres database for
|
|
// testing.
|
|
func NewTestPostgresDB(t *testing.T) *PostgresStore {
|
|
t.Helper()
|
|
|
|
t.Logf("Creating new Postgres DB for testing")
|
|
|
|
sqlFixture := NewTestPgFixture(t, DefaultPostgresFixtureLifetime)
|
|
store, err := NewPostgresStore(
|
|
sqlFixture.GetConfig(), &chaincfg.MainNetParams,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
sqlFixture.TearDown(t)
|
|
})
|
|
|
|
return store
|
|
}
|