diff --git a/Makefile b/Makefile index 44855b5..143817f 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,9 @@ DOCKER_TOOLS = docker run \ TEST_FLAGS = -test.timeout=20m +DEV_TAGS = kvdb_postgres kvdb_sqlite + + UNIT := $(GOLIST) | $(XARGS) env $(GOTEST) $(TEST_FLAGS) LDFLAGS := -X main.Commit=$(shell git describe --tags) RELEASE_LDFLAGS := -s -w -buildid= $(LDFLAGS) @@ -70,7 +73,7 @@ build: install: @$(call print, "Installing chantools.") - $(GOINSTALL) -ldflags "$(LDFLAGS)" ./... + $(GOINSTALL) -tags="$(DEV_TAGS)" -ldflags "$(LDFLAGS)" ./... release: @$(call print, "Creating release of chantools.") diff --git a/cmd/chantools/chanbackup.go b/cmd/chantools/chanbackup.go index 2cfb980..4850cda 100644 --- a/cmd/chantools/chanbackup.go +++ b/cmd/chantools/chanbackup.go @@ -3,6 +3,7 @@ package main import ( "errors" "fmt" + "path/filepath" "github.com/lightninglabs/chantools/lnd" "github.com/lightningnetwork/lnd/chanbackup" @@ -54,11 +55,17 @@ func (c *chanBackupCommand) Execute(_ *cobra.Command, _ []string) error { return errors.New("backup file is required") } - // Check that we have a channel DB. - if c.ChannelDB == "" { - return errors.New("channel DB is required") + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) } - db, err := lnd.OpenDB(c.ChannelDB, true) + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB(dbConfig, true, chainParams.Name, opts...) if err != nil { return fmt.Errorf("error opening rescue DB: %w", err) } diff --git a/cmd/chantools/deletepayments.go b/cmd/chantools/deletepayments.go index 17b4275..efe3186 100644 --- a/cmd/chantools/deletepayments.go +++ b/cmd/chantools/deletepayments.go @@ -1,8 +1,8 @@ package main import ( - "errors" "fmt" + "path/filepath" "github.com/lightninglabs/chantools/lnd" "github.com/spf13/cobra" @@ -44,11 +44,17 @@ run lnd ` + lndVersion + ` or later after using this command!'`, } func (c *deletePaymentsCommand) Execute(_ *cobra.Command, _ []string) error { - // Check that we have a channel DB. - if c.ChannelDB == "" { - return errors.New("channel DB is required") + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) } - db, err := lnd.OpenDB(c.ChannelDB, false) + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB(dbConfig, false, chainParams.Name, opts...) if err != nil { return fmt.Errorf("error opening rescue DB: %w", err) } diff --git a/cmd/chantools/dropchannelgraph.go b/cmd/chantools/dropchannelgraph.go index d7f2b8d..8b2b82f 100644 --- a/cmd/chantools/dropchannelgraph.go +++ b/cmd/chantools/dropchannelgraph.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "errors" "fmt" + "path/filepath" "time" "github.com/btcsuite/btcd/btcec/v2" @@ -60,8 +61,8 @@ chantools dropchannelgraph \ RunE: cc.Execute, } cc.cmd.Flags().StringVar( - &cc.ChannelDB, "channeldb", "", "lnd channel.db file to drop "+ - "channels from", + &cc.ChannelDB, "channeldb", "", "lnd's channel database file "+ + "to drop channels from", ) cc.cmd.Flags().Uint64Var( &cc.SingleChannel, "single_channel", 0, "the single channel "+ @@ -81,11 +82,16 @@ chantools dropchannelgraph \ } func (c *dropChannelGraphCommand) Execute(_ *cobra.Command, _ []string) error { - // Check that we have a channel DB. - if c.ChannelDB == "" { - return errors.New("channel DB is required") + dbConfig := GetDBConfig() + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) } - db, err := lnd.OpenDB(c.ChannelDB, false) + + db, err := lnd.OpenChannelDB(dbConfig, false, chainParams.Name, opts...) if err != nil { return fmt.Errorf("error opening rescue DB: %w", err) } diff --git a/cmd/chantools/dropgraphzombies.go b/cmd/chantools/dropgraphzombies.go index 7daf893..0f00817 100644 --- a/cmd/chantools/dropgraphzombies.go +++ b/cmd/chantools/dropgraphzombies.go @@ -1,8 +1,8 @@ package main import ( - "errors" "fmt" + "path/filepath" "github.com/lightninglabs/chantools/lnd" "github.com/lightningnetwork/lnd/channeldb" @@ -51,11 +51,17 @@ run lnd ` + lndVersion + ` or later after using this command!'`, } func (c *dropGraphZombiesCommand) Execute(_ *cobra.Command, _ []string) error { - // Check that we have a channel DB. - if c.ChannelDB == "" { - return errors.New("channel DB is required") + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) } - db, err := lnd.OpenDB(c.ChannelDB, false) + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB(dbConfig, false, chainParams.Name, opts...) if err != nil { return fmt.Errorf("error opening rescue DB: %w", err) } diff --git a/cmd/chantools/dumpchannels.go b/cmd/chantools/dumpchannels.go index f7b59ed..00fb2f1 100644 --- a/cmd/chantools/dumpchannels.go +++ b/cmd/chantools/dumpchannels.go @@ -3,6 +3,7 @@ package main import ( "errors" "fmt" + "path/filepath" "github.com/davecgh/go-spew/spew" "github.com/lightninglabs/chantools/dump" @@ -53,11 +54,17 @@ given lnd channel.db gile in a human readable format.`, } func (c *dumpChannelsCommand) Execute(_ *cobra.Command, _ []string) error { - // Check that we have a channel DB. - if c.ChannelDB == "" { - return errors.New("channel DB is required") + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) } - db, err := lnd.OpenDB(c.ChannelDB, true) + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB(dbConfig, false, chainParams.Name, opts...) if err != nil { return fmt.Errorf("error opening rescue DB: %w", err) } diff --git a/cmd/chantools/forceclose.go b/cmd/chantools/forceclose.go index 9e4cc57..7c58e17 100644 --- a/cmd/chantools/forceclose.go +++ b/cmd/chantools/forceclose.go @@ -4,10 +4,10 @@ import ( "bytes" "encoding/hex" "encoding/json" - "errors" "fmt" "io" "os" + "path/filepath" "time" "github.com/btcsuite/btcd/btcutil/hdkeychain" @@ -78,11 +78,17 @@ func (c *forceCloseCommand) Execute(_ *cobra.Command, _ []string) error { return fmt.Errorf("error reading root key: %w", err) } - // Check that we have a channel DB. - if c.ChannelDB == "" { - return errors.New("rescue DB is required") + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) } - db, err := lnd.OpenDB(c.ChannelDB, true) + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB(dbConfig, false, chainParams.Name, opts...) if err != nil { return fmt.Errorf("error opening rescue DB: %w", err) } diff --git a/cmd/chantools/migratedb.go b/cmd/chantools/migratedb.go index 6ef9880..97a635a 100644 --- a/cmd/chantools/migratedb.go +++ b/cmd/chantools/migratedb.go @@ -1,8 +1,8 @@ package main import ( - "errors" "fmt" + "path/filepath" "github.com/lightninglabs/chantools/lnd" "github.com/spf13/cobra" @@ -40,13 +40,19 @@ run lnd ` + lndVersion + ` or later after using this command!'`, } func (c *migrateDBCommand) Execute(_ *cobra.Command, _ []string) error { - // Check that we have a channel DB. - if c.ChannelDB == "" { - return errors.New("channel DB is required") + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) } - db, err := lnd.OpenDB(c.ChannelDB, false) + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB(dbConfig, false, chainParams.Name, opts...) if err != nil { - return fmt.Errorf("error opening DB: %w", err) + return fmt.Errorf("error opening rescue DB: %w", err) } return db.Close() diff --git a/cmd/chantools/removechannel.go b/cmd/chantools/removechannel.go index 15697ff..7dda7b3 100644 --- a/cmd/chantools/removechannel.go +++ b/cmd/chantools/removechannel.go @@ -1,8 +1,8 @@ package main import ( - "errors" "fmt" + "path/filepath" "strconv" "strings" @@ -52,13 +52,19 @@ run lnd ` + lndVersion + ` or later after using this command!`, } func (c *removeChannelCommand) Execute(_ *cobra.Command, _ []string) error { - // Check that we have a channel DB. - if c.ChannelDB == "" { - return errors.New("channel DB is required") + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) } - db, err := lnd.OpenDB(c.ChannelDB, false) + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB(dbConfig, false, chainParams.Name, opts...) if err != nil { - return fmt.Errorf("error opening channel DB: %w", err) + return fmt.Errorf("error opening rescue DB: %w", err) } defer func() { if err := db.Close(); err != nil { diff --git a/cmd/chantools/rescueclosed.go b/cmd/chantools/rescueclosed.go index 9e9dd94..0cd48d6 100644 --- a/cmd/chantools/rescueclosed.go +++ b/cmd/chantools/rescueclosed.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "regexp" "time" @@ -122,30 +123,13 @@ func (c *rescueClosedCommand) Execute(_ *cobra.Command, _ []string) error { return fmt.Errorf("error reading root key: %w", err) } + cmdErr := errors.New("you either need to specify --channeldb and " + + "--fromsummary or --force_close_addr and " + + "--commit_point but not a mixture of them") + // What way of recovery has the user chosen? From summary and DB or from // address and commit point? switch { - case c.ChannelDB != "": - db, err := lnd.OpenDB(c.ChannelDB, true) - if err != nil { - return fmt.Errorf("error opening rescue DB: %w", err) - } - - // Parse channel entries from any of the possible input files. - entries, err := c.inputs.parseInputType() - if err != nil { - return err - } - - commitPoints, err := commitPointsFromDB(db.ChannelStateDB()) - if err != nil { - return fmt.Errorf("error reading commit points from "+ - "db: %w", err) - } - return rescueClosedChannels( - c.NumKeys, extendedKey, entries, commitPoints, - ) - case c.Addr != "": // First parse address to get targetPubKeyHash from it later. targetAddr, err := btcutil.DecodeAddress(c.Addr, chainParams) @@ -185,9 +169,39 @@ func (c *rescueClosedCommand) Execute(_ *cobra.Command, _ []string) error { ) default: - return errors.New("you either need to specify --channeldb and " + - "--fromsummary or --force_close_addr and " + - "--commit_point but not a mixture of them") + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir + // from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) + } + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB( + dbConfig, false, chainParams.Name, opts..., + ) + if err != nil { + return fmt.Errorf("error opening rescue DB: %w, %w", + err, cmdErr) + } + + // Parse channel entries from any of the possible input files. + entries, err := c.inputs.parseInputType() + if err != nil { + return err + } + + commitPoints, err := commitPointsFromDB(db.ChannelStateDB()) + if err != nil { + return fmt.Errorf("error reading commit points from "+ + "db: %w", err) + } + return rescueClosedChannels( + c.NumKeys, extendedKey, entries, commitPoints, + ) } } diff --git a/cmd/chantools/rescuefunding.go b/cmd/chantools/rescuefunding.go index bb89f38..c5a49c3 100644 --- a/cmd/chantools/rescuefunding.go +++ b/cmd/chantools/rescuefunding.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "errors" "fmt" + "path/filepath" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" @@ -151,18 +152,28 @@ func (c *rescueFundingCommand) Execute(_ *cobra.Command, _ []string) error { // Check that we have a channel DB or manual keys. switch { - case (c.ChannelDB == "" || c.DBChannelPoint == "") && - c.RemotePubKey == "": + case c.DBChannelPoint == "" && c.RemotePubKey == "": + return errors.New("need to specify either channel point or " + + "both local and remote pubkey") + + case c.DBChannelPoint != "": + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir + // from it. + if c.ChannelDB != "" { + graphDir := filepath.Dir(c.ChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) + } - return errors.New("need to specify either channel DB and " + - "channel point or both local and remote pubkey") + dbConfig := GetDBConfig() - case c.ChannelDB != "" && c.DBChannelPoint != "": - db, err := lnd.OpenDB(c.ChannelDB, true) + db, err := lnd.OpenChannelDB( + dbConfig, false, chainParams.Name, opts..., + ) if err != nil { return fmt.Errorf("error opening rescue DB: %w", err) } - // Parse channel point of channel to rescue as known to the DB. databaseOp, err = lnd.ParseOutpoint(c.DBChannelPoint) if err != nil { diff --git a/cmd/chantools/root.go b/cmd/chantools/root.go index b712c2e..9339bd5 100644 --- a/cmd/chantools/root.go +++ b/cmd/chantools/root.go @@ -7,9 +7,11 @@ import ( "fmt" "io" "os" + "path/filepath" "strings" "time" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btclog" @@ -19,7 +21,10 @@ import ( "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/chanbackup" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/kvdb/postgres" + "github.com/lightningnetwork/lnd/kvdb/sqlite" "github.com/lightningnetwork/lnd/peer" + "github.com/ory/viper" "github.com/spf13/cobra" ) @@ -50,6 +55,9 @@ var ( logWriter = build.NewRotatingLogWriter() log = build.NewSubLogger("CHAN", genSubLogger(logWriter)) chainParams = &chaincfg.MainNetParams + + // defaultDataDir is the default data directory for lnd. + defaultDataDir = filepath.Join(btcutil.AppDataDir("lnd", false), "data") ) var rootCmd = &cobra.Command{ @@ -96,6 +104,45 @@ func main() { &Signet, "signet", "s", false, "Indicates if the public "+ "signet parameters should be used", ) + rootCmd.PersistentFlags().String("db.backend", "bolt", "The selected "+ + "database backend (bolt/etcd/postgres/sqlite)", + ) + + // Bolt settings + rootCmd.PersistentFlags().Duration("db.bolt.dbtimeout", 10*time.Second, + "Specify the timeout value used when opening the database", + ) + rootCmd.PersistentFlags().String("db.bolt.data-dir", defaultDataDir, + "Lnd data dir where bolt dbs are located", + ) + rootCmd.PersistentFlags().String("db.bolt.tower-dir", defaultDataDir, + "Lnd watchtower dir where bolt dbs are located", + ) + + // Sqlite settings + rootCmd.PersistentFlags().String("db.sqlite.data-dir", defaultDataDir, + "Lnd data dir where sqlite dbs are located", + ) + rootCmd.PersistentFlags().String("db.sqlite.tower-dir", defaultDataDir, + "Lnd watchtower dir where sqlite dbs are located", + ) + rootCmd.PersistentFlags().Duration("db.sqlite.timeout", 10*time.Second, + "Specify the timeout value used when opening the database", + ) + + // Postgres settings + rootCmd.PersistentFlags().String("db.postgres.dsn", "", "Postgres "+ + "connection string", + ) + rootCmd.PersistentFlags().Duration("db.postgres.timeout", + 10*time.Second, "Specify the timeout value used when opening"+ + "the database", + ) + + // Bind flags to viper + if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil { + log.Errorf("error binding flags: %v", err) + } rootCmd.AddCommand( newChanBackupCommand(), @@ -189,8 +236,9 @@ func (r *rootKey) readWithBirthday() (*hdkeychain.ExtendedKey, time.Time, return extendedKey, time.Unix(0, 0), err case r.WalletDB != "": + cfg := GetDBConfig() wallet, pw, cleanup, err := lnd.OpenWallet( - r.WalletDB, chainParams, + cfg, r.WalletDB, chainParams, ) if err != nil { return nil, time.Unix(0, 0), fmt.Errorf("error "+ @@ -275,9 +323,22 @@ func (f *inputFlags) parseInputType() ([]*dataformat.SummaryEntry, error) { target = &dataformat.SummaryEntryFile{} case f.FromChannelDB != "": - db, err := lnd.OpenDB(f.FromChannelDB, true) + var opts []lnd.DBOption + + // In case the channel DB is specified, we get the graph dir + // from it. + if f.FromChannelDB != "" { + graphDir := filepath.Dir(f.FromChannelDB) + opts = append(opts, lnd.WithCustomGraphDir(graphDir)) + } + + dbConfig := GetDBConfig() + + db, err := lnd.OpenChannelDB( + dbConfig, true, chainParams.Name, opts..., + ) if err != nil { - return nil, fmt.Errorf("error opening channel DB: %w", + return nil, fmt.Errorf("error opening rescue DB: %w", err) } target = &dataformat.ChannelDBFile{DB: db.ChannelStateDB()} @@ -365,3 +426,28 @@ func newExplorerAPI(apiURL string) *btc.ExplorerAPI { // Otherwise use the provided URL. return &btc.ExplorerAPI{BaseURL: apiURL} } + +// GetDBConfig returns the database configuration from viper/cobra flags. +func GetDBConfig() lnd.DB { + return lnd.DB{ + Backend: viper.GetString("db.backend"), + Bolt: &lnd.Bolt{ + DBTimeout: viper.GetDuration("db.bolt.dbtimeout"), + DataDir: viper.GetString("db.bolt.data-dir"), + TowerDir: viper.GetString("db.bolt.tower-dir"), + }, + Sqlite: &lnd.Sqlite{ + DataDir: viper.GetString("db.sqlite.data-dir"), + TowerDir: viper.GetString("db.sqlite.tower-dir"), + Config: &sqlite.Config{ + MaxConnections: viper.GetInt("db.sqlite.timeout"), + }, + }, + Postgres: &postgres.Config{ + Dsn: viper.GetString("db.postgres.dsn"), + MaxConnections: viper.GetInt("db.postgres.max-connections"), + Timeout: viper.GetDuration("db.postgres.timeout"), + }, + // Add Etcd config if needed + } +} diff --git a/cmd/chantools/walletinfo.go b/cmd/chantools/walletinfo.go index 9458df0..d16fa9a 100644 --- a/cmd/chantools/walletinfo.go +++ b/cmd/chantools/walletinfo.go @@ -78,13 +78,10 @@ or simply press without entering a password when being prompted.`, } func (c *walletInfoCommand) Execute(_ *cobra.Command, _ []string) error { - // Check that we have a wallet DB. - if c.WalletDB == "" { - return errors.New("wallet DB is required") - } + cfg := GetDBConfig() w, privateWalletPw, cleanup, err := lnd.OpenWallet( - c.WalletDB, chainParams, + cfg, c.WalletDB, chainParams, ) if err != nil { return fmt.Errorf("error opening wallet file '%s': %w", diff --git a/go.mod b/go.mod index 379ccb1..d93ac76 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,10 @@ require ( golang.org/x/oauth2 v0.14.0 ) -require github.com/tv42/zbase32 v0.0.0-20220222190657-f76a9fc892fa +require ( + github.com/ory/viper v1.7.5 + github.com/tv42/zbase32 v0.0.0-20220222190657-f76a9fc892fa +) require ( github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -53,6 +56,7 @@ require ( github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/btcsuite/winsvc v1.0.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/continuity v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect @@ -60,6 +64,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect github.com/decred/dcrd/lru v1.1.2 // indirect + github.com/dgraph-io/ristretto v0.0.1 // indirect github.com/docker/cli v20.10.17+incompatible // indirect github.com/docker/docker v24.0.9+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -68,6 +73,7 @@ require ( github.com/fergusstrange/embedded-postgres v1.25.0 // indirect github.com/fortytw2/leaktest v1.3.0 // indirect github.com/frankban/quicktest v1.14.3 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -86,6 +92,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -121,6 +128,7 @@ require ( github.com/lightningnetwork/lnd/sqldb v1.0.4 // indirect github.com/lightningnetwork/lnd/tlv v1.2.6 // indirect github.com/ltcsuite/ltcd v0.0.0-20191228044241-92166e412499 // indirect + github.com/magiconair/properties v1.8.1 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/miekg/dns v1.1.43 // indirect @@ -133,6 +141,7 @@ require ( github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runc v1.1.14 // indirect github.com/ory/dockertest/v3 v3.10.0 // indirect + github.com/pelletier/go-toml v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.11.1 // indirect @@ -146,8 +155,12 @@ require ( github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/sirupsen/logrus v1.9.2 // indirect github.com/soheilhy/cmux v0.1.5 // indirect + github.com/spf13/afero v1.9.2 // indirect + github.com/spf13/cast v1.3.0 // indirect + github.com/spf13/jwalterweatherman v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.2.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect @@ -193,6 +206,7 @@ require ( google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/errgo.v1 v1.0.1 // indirect + gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/macaroon-bakery.v2 v2.1.0 // indirect gopkg.in/macaroon.v2 v2.1.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index cb50828..91e9620 100644 --- a/go.sum +++ b/go.sum @@ -613,6 +613,7 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= @@ -695,6 +696,7 @@ github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -758,7 +760,11 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3 github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY= github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8= +github.com/dgraph-io/ristretto v0.0.1 h1:cJwdnj42uV8Jg4+KLrYovLiCgIfz9wtWm6E6KA+1tLs= +github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dhui/dktest v0.4.0 h1:z05UmuXZHO/bgj/ds2bGMBu8FI4WA+Ag/m3ghL+om7M= github.com/dhui/dktest v0.4.0/go.mod h1:v/Dbz1LgCBOi2Uki2nUqLBGa83hWBGFMu5MrgMDCc78= @@ -982,6 +988,7 @@ github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57Q github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -1025,6 +1032,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -1110,6 +1118,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/juju/clock v0.0.0-20220203021603-d9deb868a28a h1:Az/6CM/P5guGHNy7r6TkOCctv3lDmN3W1uhku7QMupk= @@ -1153,6 +1162,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -1217,6 +1227,7 @@ github.com/lunixbochs/vtclean v0.0.0-20160125035106-4fbf7632a2c6/go.mod h1:pHhQN github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.6/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -1290,7 +1301,10 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= +github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE= +github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= @@ -1367,19 +1381,25 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -1404,6 +1424,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= @@ -1523,6 +1544,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= @@ -1746,6 +1768,7 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2205,6 +2228,7 @@ gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/macaroon-bakery.v2 v2.1.0 h1:9Jw/+9XHBSutkaeVpWhDx38IcSNLJwWUICkOK98DHls= gopkg.in/macaroon-bakery.v2 v2.1.0/go.mod h1:B4/T17l+ZWGwxFSZQmlBwp25x+og7OkhETfr3S9MbIA= diff --git a/lnd/aezeed.go b/lnd/aezeed.go index 9c293bf..822183f 100644 --- a/lnd/aezeed.go +++ b/lnd/aezeed.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "regexp" "strings" "syscall" @@ -182,7 +183,7 @@ func PasswordFromConsole(userQuery string) ([]byte, error) { // OpenWallet opens a lnd compatible wallet and returns it, along with the // private wallet password. -func OpenWallet(walletDbPath string, +func OpenWallet(cfg DB, walletDbPath string, chainParams *chaincfg.Params) (*wallet.Wallet, []byte, func() error, error) { @@ -223,11 +224,16 @@ func OpenWallet(walletDbPath string, privateWalletPw = pw } + var opts []DBOption + + if walletDbPath != "" { + walletDir := filepath.Dir(walletDbPath) + fmt.Printf("Using wallet directory: %s\n", walletDir) + opts = append(opts, WithCustomWalletDir(walletDir)) + } + // Try to load and open the wallet. - db, err := walletdb.Open( - "bdb", lncfg.CleanAndExpandPath(walletDbPath), false, - DefaultOpenTimeout, - ) + db, err := openWalletDB(cfg, chainParams.Name, opts...) if errors.Is(err, bbolt.ErrTimeout) { return nil, nil, nil, errors.New("error opening wallet " + "database, make sure lnd is not running and holding " + @@ -265,6 +271,12 @@ func OpenWallet(walletDbPath string, return w, privateWalletPw, cleanup, nil } +func openWalletDB(cfg DB, network string, + opts ...DBOption) (walletdb.DB, error) { + + return openDB(cfg, lncfg.NSWalletDB, network, opts...) +} + // DecryptWalletRootKey decrypts a lnd compatible wallet's root key. func DecryptWalletRootKey(db walletdb.DB, privatePassphrase []byte) ([]byte, error) { diff --git a/lnd/channeldb.go b/lnd/channeldb.go index b83758f..da6178e 100644 --- a/lnd/channeldb.go +++ b/lnd/channeldb.go @@ -1,500 +1,272 @@ package lnd import ( + "context" "errors" "fmt" - "io" - "os" + "path/filepath" "time" - "github.com/btcsuite/btcwallet/walletdb" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/kvdb" + "github.com/lightningnetwork/lnd/kvdb/etcd" + "github.com/lightningnetwork/lnd/kvdb/postgres" + "github.com/lightningnetwork/lnd/kvdb/sqlbase" + "github.com/lightningnetwork/lnd/kvdb/sqlite" + "github.com/lightningnetwork/lnd/lncfg" "go.etcd.io/bbolt" ) -const ( - DefaultOpenTimeout = time.Second * 10 -) - -func OpenDB(dbPath string, readonly bool) (*channeldb.DB, error) { - backend, err := openDB(dbPath, false, readonly, DefaultOpenTimeout) - if errors.Is(err, bbolt.ErrTimeout) { - return nil, fmt.Errorf("error opening %s: make sure lnd is "+ - "not running, database is locked by another process", - dbPath) - } - if err != nil { - return nil, err - } - - return channeldb.CreateWithBackend( - backend, channeldb.OptionSetUseGraphCache(false), - channeldb.OptionNoMigration(readonly), - ) +// Bolt specifies the settings for the bolt database. +type Bolt struct { + DBTimeout time.Duration + DataDir string + TowerDir string } -// convertErr converts some bolt errors to the equivalent walletdb error. -func convertErr(err error) error { - switch { - // Database open/create errors. - case errors.Is(err, bbolt.ErrDatabaseNotOpen): - return walletdb.ErrDbNotOpen - case errors.Is(err, bbolt.ErrInvalid): - return walletdb.ErrInvalid - - // Transaction errors. - case errors.Is(err, bbolt.ErrTxNotWritable): - return walletdb.ErrTxNotWritable - case errors.Is(err, bbolt.ErrTxClosed): - return walletdb.ErrTxClosed - - // Value/bucket errors. - case errors.Is(err, bbolt.ErrBucketNotFound): - return walletdb.ErrBucketNotFound - case errors.Is(err, bbolt.ErrBucketExists): - return walletdb.ErrBucketExists - case errors.Is(err, bbolt.ErrBucketNameRequired): - return walletdb.ErrBucketNameRequired - case errors.Is(err, bbolt.ErrKeyRequired): - return walletdb.ErrKeyRequired - case errors.Is(err, bbolt.ErrKeyTooLarge): - return walletdb.ErrKeyTooLarge - case errors.Is(err, bbolt.ErrValueTooLarge): - return walletdb.ErrValueTooLarge - case errors.Is(err, bbolt.ErrIncompatibleValue): - return walletdb.ErrIncompatibleValue - } - - // Return the original error if none of the above applies. - return err +// Sqlite specifies the settings for the sqlite database. +type Sqlite struct { + DataDir string + TowerDir string + Config *sqlite.Config } -// transaction represents a database transaction. It can either by read-only or -// read-write and implements the walletdb Tx interfaces. The transaction -// provides a root bucket against which all read and writes occur. -type transaction struct { - boltTx *bbolt.Tx +// DB specifies the settings for all different database backends. +type DB struct { + Backend string + Etcd *etcd.Config + Bolt *Bolt + Postgres *postgres.Config + Sqlite *Sqlite } -func (tx *transaction) ReadBucket(key []byte) walletdb.ReadBucket { - return tx.ReadWriteBucket(key) -} - -// ForEachBucket will iterate through all top level buckets. -func (tx *transaction) ForEachBucket(fn func(key []byte) error) error { - return convertErr(tx.boltTx.ForEach( - func(name []byte, _ *bbolt.Bucket) error { - return fn(name) - }, - )) -} +const ( + // DefaultOpenTimeout is the default timeout for opening a database. + DefaultOpenTimeout = time.Second * 10 +) -func (tx *transaction) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket { - boltBucket := tx.boltTx.Bucket(key) - if boltBucket == nil { - return nil - } - return (*bucket)(boltBucket) -} +// Init should be called upon start to pre-initialize database for sql +// backends. If max connections are not set, the amount of connections will be +// unlimited however we only use one connection during the migration. +func (db DB) Init() error { + // Start embedded etcd server if requested. + switch { + case db.Backend == lncfg.PostgresBackend: + sqlbase.Init(db.Postgres.MaxConnections) -func (tx *transaction) CreateTopLevelBucket(key []byte) (walletdb.ReadWriteBucket, error) { - boltBucket, err := tx.boltTx.CreateBucketIfNotExists(key) - if err != nil { - return nil, convertErr(err) + case db.Backend == lncfg.SqliteBackend: + sqlbase.Init(db.Sqlite.Config.MaxConnections) } - return (*bucket)(boltBucket), nil -} -func (tx *transaction) DeleteTopLevelBucket(key []byte) error { - err := tx.boltTx.DeleteBucket(key) - if err != nil { - return convertErr(err) - } return nil } -// Commit commits all changes that have been made through the root bucket and -// all of its sub-buckets to persistent storage. -// -// This function is part of the walletdb.ReadWriteTx interface implementation. -func (tx *transaction) Commit() error { - return convertErr(tx.boltTx.Commit()) -} +// DBOption is a functional option for configuring the database. +type DBOption func(*dbOptions) -// Rollback undoes all changes that have been made to the root bucket and all of -// its sub-buckets. -// -// This function is part of the walletdb.ReadTx interface implementation. -func (tx *transaction) Rollback() error { - return convertErr(tx.boltTx.Rollback()) +type dbOptions struct { + customGraphDir string + customWalletDir string } -// OnCommit takes a function closure that will be executed when the transaction -// successfully gets committed. -// -// This function is part of the walletdb.ReadWriteTx interface implementation. -func (tx *transaction) OnCommit(f func()) { - tx.boltTx.OnCommit(f) +// WithCustomGraphDir sets a custom directory for the graph database. +func WithCustomGraphDir(dir string) DBOption { + return func(opts *dbOptions) { + opts.customGraphDir = dir + } } -// bucket is an internal type used to represent a collection of key/value pairs -// and implements the walletdb Bucket interfaces. -type bucket bbolt.Bucket - -// Enforce bucket implements the walletdb Bucket interfaces. -var _ walletdb.ReadWriteBucket = (*bucket)(nil) - -// NestedReadWriteBucket retrieves a nested bucket with the given key. Returns -// nil if the bucket does not exist. -// -// This function is part of the walletdb.ReadWriteBucket interface implementation. -func (b *bucket) NestedReadWriteBucket(key []byte) walletdb.ReadWriteBucket { - boltBucket := (*bbolt.Bucket)(b).Bucket(key) - // Don't return a non-nil interface to a nil pointer. - if boltBucket == nil { - return nil +// WithCustomWalletDir sets a custom directory for the wallet database. +func WithCustomWalletDir(dir string) DBOption { + return func(opts *dbOptions) { + opts.customWalletDir = dir } - return (*bucket)(boltBucket) } -func (b *bucket) NestedReadBucket(key []byte) walletdb.ReadBucket { - return b.NestedReadWriteBucket(key) -} +func openDB(cfg DB, prefix, network string, + opts ...DBOption) (kvdb.Backend, error) { -// CreateBucket creates and returns a new nested bucket with the given key. -// Returns ErrBucketExists if the bucket already exists, ErrBucketNameRequired -// if the key is empty, or ErrIncompatibleValue if the key value is otherwise -// invalid. -// -// This function is part of the walletdb.ReadWriteBucket interface implementation. -func (b *bucket) CreateBucket(key []byte) (walletdb.ReadWriteBucket, error) { - boltBucket, err := (*bbolt.Bucket)(b).CreateBucket(key) - if err != nil { - return nil, convertErr(err) - } - return (*bucket)(boltBucket), nil -} + backend := cfg.Backend -// CreateBucketIfNotExists creates and returns a new nested bucket with the -// given key if it does not already exist. Returns ErrBucketNameRequired if the -// key is empty or ErrIncompatibleValue if the key value is otherwise invalid. -// -// This function is part of the walletdb.ReadWriteBucket interface implementation. -func (b *bucket) CreateBucketIfNotExists(key []byte) (walletdb.ReadWriteBucket, error) { - boltBucket, err := (*bbolt.Bucket)(b).CreateBucketIfNotExists(key) + // Init the db connections for sql backends. + err := cfg.Init() if err != nil { - return nil, convertErr(err) + return nil, err } - return (*bucket)(boltBucket), nil -} -// DeleteNestedBucket removes a nested bucket with the given key. Returns -// ErrTxNotWritable if attempted against a read-only transaction and -// ErrBucketNotFound if the specified bucket does not exist. -// -// This function is part of the walletdb.ReadWriteBucket interface implementation. -func (b *bucket) DeleteNestedBucket(key []byte) error { - return convertErr((*bbolt.Bucket)(b).DeleteBucket(key)) -} - -// ForEach invokes the passed function with every key/value pair in the bucket. -// This includes nested buckets, in which case the value is nil, but it does not -// include the key/value pairs within those nested buckets. -// -// NOTE: The values returned by this function are only valid during a -// transaction. Attempting to access them after a transaction has ended will -// likely result in an access violation. -// -// This function is part of the walletdb.ReadBucket interface implementation. -func (b *bucket) ForEach(fn func(k, v []byte) error) error { - return convertErr((*bbolt.Bucket)(b).ForEach(fn)) -} - -// Put saves the specified key/value pair to the bucket. Keys that do not -// already exist are added and keys that already exist are overwritten. Returns -// ErrTxNotWritable if attempted against a read-only transaction. -// -// This function is part of the walletdb.ReadWriteBucket interface implementation. -func (b *bucket) Put(key, value []byte) error { - return convertErr((*bbolt.Bucket)(b).Put(key, value)) -} - -// Get returns the value for the given key. Returns nil if the key does -// not exist in this bucket (or nested buckets). -// -// NOTE: The value returned by this function is only valid during a -// transaction. Attempting to access it after a transaction has ended -// will likely result in an access violation. -// -// This function is part of the walletdb.ReadBucket interface implementation. -func (b *bucket) Get(key []byte) []byte { - return (*bbolt.Bucket)(b).Get(key) -} - -// Delete removes the specified key from the bucket. Deleting a key that does -// not exist does not return an error. Returns ErrTxNotWritable if attempted -// against a read-only transaction. -// -// This function is part of the walletdb.ReadWriteBucket interface implementation. -func (b *bucket) Delete(key []byte) error { - return convertErr((*bbolt.Bucket)(b).Delete(key)) -} - -func (b *bucket) ReadCursor() walletdb.ReadCursor { - return b.ReadWriteCursor() -} - -// ReadWriteCursor returns a new cursor, allowing for iteration over the bucket's -// key/value pairs and nested buckets in forward or backward order. -// -// This function is part of the walletdb.ReadWriteBucket interface implementation. -func (b *bucket) ReadWriteCursor() walletdb.ReadWriteCursor { - return (*cursor)((*bbolt.Bucket)(b).Cursor()) -} - -// Tx returns the bucket's transaction. -// -// This function is part of the walletdb.ReadWriteBucket interface implementation. -func (b *bucket) Tx() walletdb.ReadWriteTx { - return &transaction{ - (*bbolt.Bucket)(b).Tx(), + options := &dbOptions{} + for _, opt := range opts { + opt(options) } -} - -// NextSequence returns an autoincrementing integer for the bucket. -func (b *bucket) NextSequence() (uint64, error) { - return (*bbolt.Bucket)(b).NextSequence() -} - -// SetSequence updates the sequence number for the bucket. -func (b *bucket) SetSequence(v uint64) error { - return (*bbolt.Bucket)(b).SetSequence(v) -} - -// Sequence returns the current integer for the bucket without incrementing it. -func (b *bucket) Sequence() uint64 { - return (*bbolt.Bucket)(b).Sequence() -} - -// cursor represents a cursor over key/value pairs and nested buckets of a -// bucket. -// -// Note that open cursors are not tracked on bucket changes and any -// modifications to the bucket, with the exception of cursor.Delete, invalidate -// the cursor. After invalidation, the cursor must be repositioned, or the keys -// and values returned may be unpredictable. -type cursor bbolt.Cursor - -// Delete removes the current key/value pair the cursor is at without -// invalidating the cursor. Returns ErrTxNotWritable if attempted on a read-only -// transaction, or ErrIncompatibleValue if attempted when the cursor points to a -// nested bucket. -// -// This function is part of the walletdb.ReadWriteCursor interface implementation. -func (c *cursor) Delete() error { - return convertErr((*bbolt.Cursor)(c).Delete()) -} -// First positions the cursor at the first key/value pair and returns the pair. -// -// This function is part of the walletdb.ReadCursor interface implementation. -func (c *cursor) First() ([]byte, []byte) { - return (*bbolt.Cursor)(c).First() -} - -// Last positions the cursor at the last key/value pair and returns the pair. -// -// This function is part of the walletdb.ReadCursor interface implementation. -func (c *cursor) Last() ([]byte, []byte) { - return (*bbolt.Cursor)(c).Last() -} - -// Next moves the cursor one key/value pair forward and returns the new pair. -// -// This function is part of the walletdb.ReadCursor interface implementation. -func (c *cursor) Next() ([]byte, []byte) { - return (*bbolt.Cursor)(c).Next() -} + // Settings to open a particular db backend. + var args []interface{} + + switch backend { + case lncfg.BoltBackend: + // Directories where the db files are located. + var graphDir string + if options.customGraphDir != "" { + graphDir = options.customGraphDir + } else { + graphDir = filepath.Join( + cfg.Bolt.DataDir, "graph", network, + ) + } -// Prev moves the cursor one key/value pair backward and returns the new pair. -// -// This function is part of the walletdb.ReadCursor interface implementation. -func (c *cursor) Prev() ([]byte, []byte) { - return (*bbolt.Cursor)(c).Prev() -} + var walletDir string + if options.customWalletDir != "" { + walletDir = options.customWalletDir + } else { + walletDir = filepath.Join( + cfg.Bolt.DataDir, "chain", "bitcoin", network, + ) + } -// Seek positions the cursor at the passed seek key. If the key does not exist, -// the cursor is moved to the next key after seek. Returns the new pair. -// -// This function is part of the walletdb.ReadCursor interface implementation. -func (c *cursor) Seek(seek []byte) ([]byte, []byte) { - return (*bbolt.Cursor)(c).Seek(seek) -} + towerServerDir := filepath.Join( + cfg.Bolt.TowerDir, "bitcoin", network, + ) -// backend represents a collection of namespaces which are persisted and -// implements the kvdb.Backend interface. All database access is performed -// through transactions which are obtained through the specific Namespace. -type backend struct { - db *bbolt.DB -} + // Path to the db file. + var path string + switch prefix { + case lncfg.NSChannelDB: + path = filepath.Join(graphDir, lncfg.ChannelDBName) -// Enforce db implements the kvdb.Backend interface. -var _ kvdb.Backend = (*backend)(nil) + case lncfg.NSMacaroonDB: + path = filepath.Join(walletDir, lncfg.MacaroonDBName) -func (b *backend) beginTx(writable bool) (*transaction, error) { - boltTx, err := b.db.Begin(writable) - if err != nil { - return nil, convertErr(err) - } - return &transaction{boltTx: boltTx}, nil -} + case lncfg.NSDecayedLogDB: + path = filepath.Join(graphDir, lncfg.DecayedLogDbName) -func (b *backend) BeginReadTx() (walletdb.ReadTx, error) { - return b.beginTx(false) -} + case lncfg.NSTowerClientDB: + path = filepath.Join(graphDir, lncfg.TowerClientDBName) -func (b *backend) BeginReadWriteTx() (walletdb.ReadWriteTx, error) { - return b.beginTx(true) -} + case lncfg.NSTowerServerDB: + path = filepath.Join( + towerServerDir, lncfg.TowerServerDBName, + ) -// Copy writes a copy of the database to the provided writer. This call will -// start a read-only transaction to perform all operations. -// -// This function is part of the kvdb.Backend interface implementation. -func (b *backend) Copy(w io.Writer) error { - return convertErr(b.db.View(func(tx *bbolt.Tx) error { - return tx.Copy(w) - })) -} + case lncfg.NSWalletDB: + path = filepath.Join(walletDir, lncfg.WalletDBName) + } -// Close cleanly shuts down the database and syncs all data. -// -// This function is part of the kvdb.Backend interface implementation. -func (b *backend) Close() error { - return convertErr(b.db.Close()) -} + const ( + noFreelistSync = true + timeout = time.Minute + ) -// Batch is similar to the package-level Update method, but it will attempt to -// optimistically combine the invocation of several transaction functions into a -// single db write transaction. -// -// This function is part of the walletdb.Db interface implementation. -func (b *backend) Batch(f func(tx walletdb.ReadWriteTx) error) error { - return b.db.Batch(func(btx *bbolt.Tx) error { - interfaceTx := transaction{btx} - - return f(&interfaceTx) - }) -} + args = []interface{}{ + path, noFreelistSync, timeout, + } + backend = kvdb.BoltBackendName -// View opens a database read transaction and executes the function f with the -// transaction passed as a parameter. After f exits, the transaction is rolled -// back. If f errors, its error is returned, not a rollback error (if any -// occur). The passed reset function is called before the start of the -// transaction and can be used to reset intermediate state. As callers may -// expect retries of the f closure (depending on the database backend used), the -// reset function will be called before each retry respectively. -func (b *backend) View(f func(tx walletdb.ReadTx) error, reset func()) error { - // We don't do any retries with bolt so we just initially call the reset - // function once. - reset() - - tx, err := b.BeginReadTx() - if err != nil { - return err - } + case kvdb.EtcdBackendName: + args = []interface{}{ + context.Background(), + cfg.Etcd.CloneWithSubNamespace(prefix), + } - // Make sure the transaction rolls back in the event of a panic. - defer func() { - if tx != nil { - _ = tx.Rollback() + case kvdb.PostgresBackendName: + args = []interface{}{ + context.Background(), + &postgres.Config{ + Dsn: cfg.Postgres.Dsn, + Timeout: time.Minute, + MaxConnections: 10, + }, + prefix, } - }() - err = f(tx) - rollbackErr := tx.Rollback() - if err != nil { - return err - } + case kvdb.SqliteBackendName: + // Directories where the db files are located. + var graphDir string + if options.customGraphDir != "" { + graphDir = options.customGraphDir + } else { + graphDir = filepath.Join( + cfg.Sqlite.DataDir, "graph", network, + ) + } - if rollbackErr != nil { - return rollbackErr - } - return nil -} + var walletDir string + if options.customWalletDir != "" { + walletDir = options.customWalletDir + } else { + walletDir = filepath.Join( + cfg.Sqlite.DataDir, "chain", "bitcoin", network, + ) + } -// Update opens a database read/write transaction and executes the function f -// with the transaction passed as a parameter. After f exits, if f did not -// error, the transaction is committed. Otherwise, if f did error, the -// transaction is rolled back. If the rollback fails, the original error -// returned by f is still returned. If the commit fails, the commit error is -// returned. As callers may expect retries of the f closure (depending on the -// database backend used), the reset function will be called before each retry -// respectively. -func (b *backend) Update(f func(tx walletdb.ReadWriteTx) error, - reset func()) error { - - // We don't do any retries with bolt so we just initially call the reset - // function once. - reset() - - tx, err := b.BeginReadWriteTx() - if err != nil { - return err - } + towerServerDir := filepath.Join( + cfg.Sqlite.TowerDir, "bitcoin", network, + ) + + var dbName string + var path string + switch prefix { + case lncfg.NSChannelDB: + path = graphDir + dbName = lncfg.SqliteChannelDBName + case lncfg.NSMacaroonDB: + path = walletDir + dbName = lncfg.SqliteChainDBName + + case lncfg.NSDecayedLogDB: + path = graphDir + dbName = lncfg.SqliteChannelDBName + + case lncfg.NSTowerClientDB: + path = graphDir + dbName = lncfg.SqliteChannelDBName + + case lncfg.NSTowerServerDB: + path = towerServerDir + dbName = lncfg.SqliteChannelDBName + + case lncfg.NSWalletDB: + path = walletDir + dbName = lncfg.SqliteChainDBName + + case lncfg.NSNeutrinoDB: + dbName = lncfg.SqliteNeutrinoDBName + } - // Make sure the transaction rolls back in the event of a panic. - defer func() { - if tx != nil { - _ = tx.Rollback() + args = []interface{}{ + context.Background(), + &sqlite.Config{ + Timeout: time.Minute, + }, + path, + dbName, + prefix, } - }() - err = f(tx) - if err != nil { - // Want to return the original error, not a rollback error if - // any occur. - _ = tx.Rollback() - return err + default: + return nil, fmt.Errorf("unknown backend: %v", backend) } - return tx.Commit() + return kvdb.Open(backend, args...) } -// PrintStats returns all collected stats pretty printed into a string. -func (b *backend) PrintStats() string { - return "n/a" -} +// OpenChannelDB opens the channel database for all supported backends. +func OpenChannelDB(cfg DB, readonly bool, network string, + opts ...DBOption) (*channeldb.DB, error) { -// filesExists reports whether the named file or directory exists. -func fileExists(name string) bool { - if _, err := os.Stat(name); err != nil { - if os.IsNotExist(err) { - return false - } - } - return true -} + backend, err := openDB(cfg, lncfg.NSChannelDB, network, opts...) -// openDB opens the database at the provided path. walletdb.ErrDbDoesNotExist -// is returned if the database doesn't exist and the create flag is not set. -func openDB(dbPath string, noFreelistSync bool, - readonly bool, timeout time.Duration) (kvdb.Backend, error) { - - if !fileExists(dbPath) { - return nil, walletdb.ErrDbDoesNotExist + // For the bbolt backend, we signal a detailed error message if the + // database is locked by another process. + if errors.Is(err, bbolt.ErrTimeout) { + return nil, errors.New("error opening boltDB: make sure lnd " + + "is not running, database is locked by another process") } - - // Specify bbolt freelist options to reduce heap pressure in case the - // freelist grows to be very large. - options := &bbolt.Options{ - NoFreelistSync: noFreelistSync, - FreelistType: bbolt.FreelistMapType, - Timeout: timeout, - ReadOnly: readonly, + if err != nil { + return nil, err } - boltDB, err := bbolt.Open(dbPath, 0600, options) - return &backend{db: boltDB}, convertErr(err) + return channeldb.CreateWithBackend( + backend, channeldb.OptionSetUseGraphCache(false), + channeldb.OptionNoMigration(readonly), + ) }