mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-05-07 18:30:21 +02:00
multi: make macaroon DB remote compatible
The macaroon root keys should also be stored to the remote database if a replicated backend such as etcd is used. This commit refactors the macaroons service and wallet unlocker to accept a kvdb backend directly instead of creating the bolt instance automatically.
This commit is contained in:
parent
0d3647d715
commit
f7b17df452
48
lncfg/db.go
48
lncfg/db.go
@ -10,7 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
dbName = "channel.db"
|
channelDBName = "channel.db"
|
||||||
|
macaroonDBName = "macaroons.db"
|
||||||
|
|
||||||
BoltBackend = "bolt"
|
BoltBackend = "bolt"
|
||||||
EtcdBackend = "etcd"
|
EtcdBackend = "etcd"
|
||||||
@ -19,6 +20,9 @@ const (
|
|||||||
// NSChannelDB is the namespace name that we use for the combined graph
|
// NSChannelDB is the namespace name that we use for the combined graph
|
||||||
// and channel state DB.
|
// and channel state DB.
|
||||||
NSChannelDB = "channeldb"
|
NSChannelDB = "channeldb"
|
||||||
|
|
||||||
|
// NSMacaroonDB is the namespace name that we use for the macaroon DB.
|
||||||
|
NSMacaroonDB = "macaroondb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DB holds database configuration for LND.
|
// DB holds database configuration for LND.
|
||||||
@ -100,6 +104,10 @@ type DatabaseBackends struct {
|
|||||||
// contains the chain height hint related data.
|
// contains the chain height hint related data.
|
||||||
HeightHintDB kvdb.Backend
|
HeightHintDB kvdb.Backend
|
||||||
|
|
||||||
|
// MacaroonDB points to a database backend that stores the macaroon root
|
||||||
|
// keys.
|
||||||
|
MacaroonDB kvdb.Backend
|
||||||
|
|
||||||
// Remote indicates whether the database backends are remote, possibly
|
// Remote indicates whether the database backends are remote, possibly
|
||||||
// replicated instances or local bbolt backed databases.
|
// replicated instances or local bbolt backed databases.
|
||||||
Remote bool
|
Remote bool
|
||||||
@ -110,14 +118,27 @@ type DatabaseBackends struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetBackends returns a set of kvdb.Backends as set in the DB config.
|
// GetBackends returns a set of kvdb.Backends as set in the DB config.
|
||||||
func (db *DB) GetBackends(ctx context.Context, dbPath string) (
|
func (db *DB) GetBackends(ctx context.Context, chanDBPath,
|
||||||
*DatabaseBackends, error) {
|
walletDBPath string) (*DatabaseBackends, error) {
|
||||||
|
|
||||||
// We keep track of all the kvdb backends we actually open and return a
|
// We keep track of all the kvdb backends we actually open and return a
|
||||||
// reference to their close function so they can be cleaned up properly
|
// reference to their close function so they can be cleaned up properly
|
||||||
// on error or shutdown.
|
// on error or shutdown.
|
||||||
closeFuncs := make(map[string]func() error)
|
closeFuncs := make(map[string]func() error)
|
||||||
|
|
||||||
|
// If we need to return early because of an error, we invoke any close
|
||||||
|
// function that has been initialized so far.
|
||||||
|
returnEarly := true
|
||||||
|
defer func() {
|
||||||
|
if !returnEarly {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, closeFunc := range closeFuncs {
|
||||||
|
_ = closeFunc()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if db.Backend == EtcdBackend {
|
if db.Backend == EtcdBackend {
|
||||||
etcdBackend, err := kvdb.Open(
|
etcdBackend, err := kvdb.Open(
|
||||||
kvdb.EtcdBackendName, ctx, db.Etcd,
|
kvdb.EtcdBackendName, ctx, db.Etcd,
|
||||||
@ -127,10 +148,12 @@ func (db *DB) GetBackends(ctx context.Context, dbPath string) (
|
|||||||
}
|
}
|
||||||
closeFuncs[NSChannelDB] = etcdBackend.Close
|
closeFuncs[NSChannelDB] = etcdBackend.Close
|
||||||
|
|
||||||
|
returnEarly = false
|
||||||
return &DatabaseBackends{
|
return &DatabaseBackends{
|
||||||
GraphDB: etcdBackend,
|
GraphDB: etcdBackend,
|
||||||
ChanStateDB: etcdBackend,
|
ChanStateDB: etcdBackend,
|
||||||
HeightHintDB: etcdBackend,
|
HeightHintDB: etcdBackend,
|
||||||
|
MacaroonDB: etcdBackend,
|
||||||
Remote: true,
|
Remote: true,
|
||||||
CloseFuncs: closeFuncs,
|
CloseFuncs: closeFuncs,
|
||||||
}, nil
|
}, nil
|
||||||
@ -138,8 +161,8 @@ func (db *DB) GetBackends(ctx context.Context, dbPath string) (
|
|||||||
|
|
||||||
// We're using all bbolt based databases by default.
|
// We're using all bbolt based databases by default.
|
||||||
boltBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
|
boltBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
|
||||||
DBPath: dbPath,
|
DBPath: chanDBPath,
|
||||||
DBFileName: dbName,
|
DBFileName: channelDBName,
|
||||||
DBTimeout: db.Bolt.DBTimeout,
|
DBTimeout: db.Bolt.DBTimeout,
|
||||||
NoFreelistSync: !db.Bolt.SyncFreelist,
|
NoFreelistSync: !db.Bolt.SyncFreelist,
|
||||||
AutoCompact: db.Bolt.AutoCompact,
|
AutoCompact: db.Bolt.AutoCompact,
|
||||||
@ -150,10 +173,25 @@ func (db *DB) GetBackends(ctx context.Context, dbPath string) (
|
|||||||
}
|
}
|
||||||
closeFuncs[NSChannelDB] = boltBackend.Close
|
closeFuncs[NSChannelDB] = boltBackend.Close
|
||||||
|
|
||||||
|
macaroonBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
|
||||||
|
DBPath: walletDBPath,
|
||||||
|
DBFileName: macaroonDBName,
|
||||||
|
DBTimeout: db.Bolt.DBTimeout,
|
||||||
|
NoFreelistSync: !db.Bolt.SyncFreelist,
|
||||||
|
AutoCompact: db.Bolt.AutoCompact,
|
||||||
|
AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error opening macaroon DB: %v", err)
|
||||||
|
}
|
||||||
|
closeFuncs[NSMacaroonDB] = macaroonBackend.Close
|
||||||
|
|
||||||
|
returnEarly = false
|
||||||
return &DatabaseBackends{
|
return &DatabaseBackends{
|
||||||
GraphDB: boltBackend,
|
GraphDB: boltBackend,
|
||||||
ChanStateDB: boltBackend,
|
ChanStateDB: boltBackend,
|
||||||
HeightHintDB: boltBackend,
|
HeightHintDB: boltBackend,
|
||||||
|
MacaroonDB: macaroonBackend,
|
||||||
CloseFuncs: closeFuncs,
|
CloseFuncs: closeFuncs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
19
lnd.go
19
lnd.go
@ -480,6 +480,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
pwService.SetLoaderOpts([]btcwallet.LoaderOption{loaderOpt})
|
pwService.SetLoaderOpts([]btcwallet.LoaderOption{loaderOpt})
|
||||||
|
pwService.SetMacaroonDB(dbs.macaroonDB)
|
||||||
walletExists, err := pwService.WalletExists()
|
walletExists, err := pwService.WalletExists()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -584,8 +585,8 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
|
|||||||
if !cfg.NoMacaroons {
|
if !cfg.NoMacaroons {
|
||||||
// Create the macaroon authentication/authorization service.
|
// Create the macaroon authentication/authorization service.
|
||||||
macaroonService, err = macaroons.NewService(
|
macaroonService, err = macaroons.NewService(
|
||||||
cfg.networkDir, "lnd", walletInitParams.StatelessInit,
|
dbs.macaroonDB, "lnd", walletInitParams.StatelessInit,
|
||||||
cfg.DB.Bolt.DBTimeout, macaroons.IPLockChecker,
|
macaroons.IPLockChecker,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("unable to set up macaroon "+
|
err := fmt.Errorf("unable to set up macaroon "+
|
||||||
@ -1326,11 +1327,6 @@ type WalletUnlockParams struct {
|
|||||||
// createWalletUnlockerService creates a WalletUnlockerService from the passed
|
// createWalletUnlockerService creates a WalletUnlockerService from the passed
|
||||||
// config.
|
// config.
|
||||||
func createWalletUnlockerService(cfg *Config) *walletunlocker.UnlockerService {
|
func createWalletUnlockerService(cfg *Config) *walletunlocker.UnlockerService {
|
||||||
chainConfig := cfg.Bitcoin
|
|
||||||
if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain {
|
|
||||||
chainConfig = cfg.Litecoin
|
|
||||||
}
|
|
||||||
|
|
||||||
// The macaroonFiles are passed to the wallet unlocker so they can be
|
// The macaroonFiles are passed to the wallet unlocker so they can be
|
||||||
// deleted and recreated in case the root macaroon key is also changed
|
// deleted and recreated in case the root macaroon key is also changed
|
||||||
// during the change password operation.
|
// during the change password operation.
|
||||||
@ -1339,8 +1335,7 @@ func createWalletUnlockerService(cfg *Config) *walletunlocker.UnlockerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return walletunlocker.New(
|
return walletunlocker.New(
|
||||||
chainConfig.ChainDir, cfg.ActiveNetParams.Params,
|
cfg.ActiveNetParams.Params, macaroonFiles,
|
||||||
!cfg.SyncFreelist, macaroonFiles, cfg.DB.Bolt.DBTimeout,
|
|
||||||
cfg.ResetWalletTransactions, nil,
|
cfg.ResetWalletTransactions, nil,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1620,6 +1615,7 @@ type databaseInstances struct {
|
|||||||
graphDB *channeldb.DB
|
graphDB *channeldb.DB
|
||||||
chanStateDB *channeldb.DB
|
chanStateDB *channeldb.DB
|
||||||
heightHintDB kvdb.Backend
|
heightHintDB kvdb.Backend
|
||||||
|
macaroonDB kvdb.Backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializeDatabases extracts the current databases that we'll use for normal
|
// initializeDatabases extracts the current databases that we'll use for normal
|
||||||
@ -1639,7 +1635,9 @@ func initializeDatabases(ctx context.Context,
|
|||||||
|
|
||||||
startOpenTime := time.Now()
|
startOpenTime := time.Now()
|
||||||
|
|
||||||
databaseBackends, err := cfg.DB.GetBackends(ctx, cfg.graphDatabaseDir())
|
databaseBackends, err := cfg.DB.GetBackends(
|
||||||
|
ctx, cfg.graphDatabaseDir(), cfg.networkDir,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("unable to obtain database "+
|
return nil, nil, fmt.Errorf("unable to obtain database "+
|
||||||
"backends: %v", err)
|
"backends: %v", err)
|
||||||
@ -1650,6 +1648,7 @@ func initializeDatabases(ctx context.Context,
|
|||||||
// within that DB.
|
// within that DB.
|
||||||
dbs := &databaseInstances{
|
dbs := &databaseInstances{
|
||||||
heightHintDB: databaseBackends.HeightHintDB,
|
heightHintDB: databaseBackends.HeightHintDB,
|
||||||
|
macaroonDB: databaseBackends.MacaroonDB,
|
||||||
}
|
}
|
||||||
cleanUp := func() {
|
cleanUp := func() {
|
||||||
// We can just close the returned close functions directly. Even
|
// We can just close the returned close functions directly. Even
|
||||||
|
@ -4,9 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
@ -17,10 +14,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DBFilename is the filename within the data directory which contains
|
|
||||||
// the macaroon stores.
|
|
||||||
DBFilename = "macaroons.db"
|
|
||||||
|
|
||||||
// ErrMissingRootKeyID specifies the root key ID is missing.
|
// ErrMissingRootKeyID specifies the root key ID is missing.
|
||||||
ErrMissingRootKeyID = fmt.Errorf("missing root key ID")
|
ErrMissingRootKeyID = fmt.Errorf("missing root key ID")
|
||||||
|
|
||||||
@ -68,34 +61,17 @@ type Service struct {
|
|||||||
StatelessInit bool
|
StatelessInit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns a service backed by the macaroon Bolt DB stored in the
|
// NewService returns a service backed by the macaroon DB backend. The `checks`
|
||||||
// passed directory. The `checks` argument can be any of the `Checker` type
|
// argument can be any of the `Checker` type functions defined in this package,
|
||||||
// functions defined in this package, or a custom checker if desired. This
|
// or a custom checker if desired. This constructor prevents double-registration
|
||||||
// constructor prevents double-registration of checkers to prevent panics, so
|
// of checkers to prevent panics, so listing the same checker more than once is
|
||||||
// listing the same checker more than once is not harmful. Default checkers,
|
// not harmful. Default checkers, such as those for `allow`, `time-before`,
|
||||||
// such as those for `allow`, `time-before`, `declared`, and `error` caveats
|
// `declared`, and `error` caveats are registered automatically and don't need
|
||||||
// are registered automatically and don't need to be added.
|
// to be added.
|
||||||
func NewService(dir, location string, statelessInit bool,
|
func NewService(db kvdb.Backend, location string, statelessInit bool,
|
||||||
dbTimeout time.Duration, checks ...Checker) (*Service, error) {
|
checks ...Checker) (*Service, error) {
|
||||||
|
|
||||||
// Ensure that the path to the directory exists.
|
rootKeyStore, err := NewRootKeyStorage(db)
|
||||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
|
||||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the database that we'll use to store the primary macaroon key,
|
|
||||||
// and all generated macaroons+caveats.
|
|
||||||
macaroonDB, err := kvdb.Create(
|
|
||||||
kvdb.BoltBackendName, path.Join(dir, DBFilename), true,
|
|
||||||
dbTimeout,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rootKeyStore, err := NewRootKeyStorage(macaroonDB)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ var (
|
|||||||
// default password of 'hello'. Only the path to the temporary
|
// default password of 'hello'. Only the path to the temporary
|
||||||
// DB file is returned, because the service will open the file
|
// DB file is returned, because the service will open the file
|
||||||
// and read the store on its own.
|
// and read the store on its own.
|
||||||
func setupTestRootKeyStorage(t *testing.T) string {
|
func setupTestRootKeyStorage(t *testing.T) (string, kvdb.Backend) {
|
||||||
tempDir, err := ioutil.TempDir("", "macaroonstore-")
|
tempDir, err := ioutil.TempDir("", "macaroonstore-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating temp dir: %v", err)
|
t.Fatalf("Error creating temp dir: %v", err)
|
||||||
@ -55,21 +55,20 @@ func setupTestRootKeyStorage(t *testing.T) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating unlock: %v", err)
|
t.Fatalf("error creating unlock: %v", err)
|
||||||
}
|
}
|
||||||
return tempDir
|
return tempDir, db
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNewService tests the creation of the macaroon service.
|
// TestNewService tests the creation of the macaroon service.
|
||||||
func TestNewService(t *testing.T) {
|
func TestNewService(t *testing.T) {
|
||||||
// First, initialize a dummy DB file with a store that the service
|
// First, initialize a dummy DB file with a store that the service
|
||||||
// can read from. Make sure the file is removed in the end.
|
// can read from. Make sure the file is removed in the end.
|
||||||
tempDir := setupTestRootKeyStorage(t)
|
tempDir, db := setupTestRootKeyStorage(t)
|
||||||
defer os.RemoveAll(tempDir)
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
// Second, create the new service instance, unlock it and pass in a
|
// Second, create the new service instance, unlock it and pass in a
|
||||||
// checker that we expect it to add to the bakery.
|
// checker that we expect it to add to the bakery.
|
||||||
service, err := macaroons.NewService(
|
service, err := macaroons.NewService(
|
||||||
tempDir, "lnd", false, kvdb.DefaultDBTimeout,
|
db, "lnd", false, macaroons.IPLockChecker,
|
||||||
macaroons.IPLockChecker,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating new service: %v", err)
|
t.Fatalf("Error creating new service: %v", err)
|
||||||
@ -117,11 +116,10 @@ func TestNewService(t *testing.T) {
|
|||||||
// incoming context.
|
// incoming context.
|
||||||
func TestValidateMacaroon(t *testing.T) {
|
func TestValidateMacaroon(t *testing.T) {
|
||||||
// First, initialize the service and unlock it.
|
// First, initialize the service and unlock it.
|
||||||
tempDir := setupTestRootKeyStorage(t)
|
tempDir, db := setupTestRootKeyStorage(t)
|
||||||
defer os.RemoveAll(tempDir)
|
defer os.RemoveAll(tempDir)
|
||||||
service, err := macaroons.NewService(
|
service, err := macaroons.NewService(
|
||||||
tempDir, "lnd", false, kvdb.DefaultDBTimeout,
|
db, "lnd", false, macaroons.IPLockChecker,
|
||||||
macaroons.IPLockChecker,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating new service: %v", err)
|
t.Fatalf("Error creating new service: %v", err)
|
||||||
@ -175,14 +173,13 @@ func TestValidateMacaroon(t *testing.T) {
|
|||||||
func TestListMacaroonIDs(t *testing.T) {
|
func TestListMacaroonIDs(t *testing.T) {
|
||||||
// First, initialize a dummy DB file with a store that the service
|
// First, initialize a dummy DB file with a store that the service
|
||||||
// can read from. Make sure the file is removed in the end.
|
// can read from. Make sure the file is removed in the end.
|
||||||
tempDir := setupTestRootKeyStorage(t)
|
tempDir, db := setupTestRootKeyStorage(t)
|
||||||
defer os.RemoveAll(tempDir)
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
// Second, create the new service instance, unlock it and pass in a
|
// Second, create the new service instance, unlock it and pass in a
|
||||||
// checker that we expect it to add to the bakery.
|
// checker that we expect it to add to the bakery.
|
||||||
service, err := macaroons.NewService(
|
service, err := macaroons.NewService(
|
||||||
tempDir, "lnd", false, kvdb.DefaultDBTimeout,
|
db, "lnd", false, macaroons.IPLockChecker,
|
||||||
macaroons.IPLockChecker,
|
|
||||||
)
|
)
|
||||||
require.NoError(t, err, "Error creating new service")
|
require.NoError(t, err, "Error creating new service")
|
||||||
defer service.Close()
|
defer service.Close()
|
||||||
@ -208,14 +205,13 @@ func TestDeleteMacaroonID(t *testing.T) {
|
|||||||
|
|
||||||
// First, initialize a dummy DB file with a store that the service
|
// First, initialize a dummy DB file with a store that the service
|
||||||
// can read from. Make sure the file is removed in the end.
|
// can read from. Make sure the file is removed in the end.
|
||||||
tempDir := setupTestRootKeyStorage(t)
|
tempDir, db := setupTestRootKeyStorage(t)
|
||||||
defer os.RemoveAll(tempDir)
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
// Second, create the new service instance, unlock it and pass in a
|
// Second, create the new service instance, unlock it and pass in a
|
||||||
// checker that we expect it to add to the bakery.
|
// checker that we expect it to add to the bakery.
|
||||||
service, err := macaroons.NewService(
|
service, err := macaroons.NewService(
|
||||||
tempDir, "lnd", false, kvdb.DefaultDBTimeout,
|
db, "lnd", false, macaroons.IPLockChecker,
|
||||||
macaroons.IPLockChecker,
|
|
||||||
)
|
)
|
||||||
require.NoError(t, err, "Error creating new service")
|
require.NoError(t, err, "Error creating new service")
|
||||||
defer service.Close()
|
defer service.Close()
|
||||||
|
@ -66,7 +66,6 @@ type RootKeyStorage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewRootKeyStorage creates a RootKeyStorage instance.
|
// NewRootKeyStorage creates a RootKeyStorage instance.
|
||||||
// TODO(aakselrod): Add support for encryption of data with passphrase.
|
|
||||||
func NewRootKeyStorage(db kvdb.Backend) (*RootKeyStorage, error) {
|
func NewRootKeyStorage(db kvdb.Backend) (*RootKeyStorage, error) {
|
||||||
// If the store's bucket doesn't exist, create it.
|
// If the store's bucket doesn't exist, create it.
|
||||||
err := kvdb.Update(db, func(tx kvdb.RwTx) error {
|
err := kvdb.Update(db, func(tx kvdb.RwTx) error {
|
||||||
@ -78,7 +77,10 @@ func NewRootKeyStorage(db kvdb.Backend) (*RootKeyStorage, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the DB wrapped in a RootKeyStorage object.
|
// Return the DB wrapped in a RootKeyStorage object.
|
||||||
return &RootKeyStorage{Backend: db, encKey: nil}, nil
|
return &RootKeyStorage{
|
||||||
|
Backend: db,
|
||||||
|
encKey: nil,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUnlock sets an encryption key if one is not already set, otherwise it
|
// CreateUnlock sets an encryption key if one is not already set, otherwise it
|
||||||
@ -97,7 +99,7 @@ func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
|
|||||||
return ErrPasswordRequired
|
return ErrPasswordRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
return kvdb.Update(r, func(tx kvdb.RwTx) error {
|
return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
|
||||||
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrRootKeyBucketNotFound
|
return ErrRootKeyBucketNotFound
|
||||||
@ -153,7 +155,7 @@ func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error {
|
|||||||
return ErrPasswordRequired
|
return ErrPasswordRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
return kvdb.Update(r, func(tx kvdb.RwTx) error {
|
return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
|
||||||
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrRootKeyBucketNotFound
|
return ErrRootKeyBucketNotFound
|
||||||
@ -225,7 +227,7 @@ func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
|
|||||||
return nil, ErrStoreLocked
|
return nil, ErrStoreLocked
|
||||||
}
|
}
|
||||||
var rootKey []byte
|
var rootKey []byte
|
||||||
err := kvdb.View(r, func(tx kvdb.RTx) error {
|
err := kvdb.View(r.Backend, func(tx kvdb.RTx) error {
|
||||||
bucket := tx.ReadBucket(rootKeyBucketName)
|
bucket := tx.ReadBucket(rootKeyBucketName)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrRootKeyBucketNotFound
|
return ErrRootKeyBucketNotFound
|
||||||
@ -276,7 +278,7 @@ func (r *RootKeyStorage) RootKey(ctx context.Context) ([]byte, []byte, error) {
|
|||||||
return nil, nil, ErrKeyValueForbidden
|
return nil, nil, ErrKeyValueForbidden
|
||||||
}
|
}
|
||||||
|
|
||||||
err = kvdb.Update(r, func(tx kvdb.RwTx) error {
|
err = kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
|
||||||
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrRootKeyBucketNotFound
|
return ErrRootKeyBucketNotFound
|
||||||
@ -319,7 +321,7 @@ func (r *RootKeyStorage) GenerateNewRootKey() error {
|
|||||||
if r.encKey == nil {
|
if r.encKey == nil {
|
||||||
return ErrStoreLocked
|
return ErrStoreLocked
|
||||||
}
|
}
|
||||||
return kvdb.Update(r, func(tx kvdb.RwTx) error {
|
return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
|
||||||
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrRootKeyBucketNotFound
|
return ErrRootKeyBucketNotFound
|
||||||
@ -341,7 +343,13 @@ func (r *RootKeyStorage) Close() error {
|
|||||||
r.encKey.Zero()
|
r.encKey.Zero()
|
||||||
r.encKey = nil
|
r.encKey = nil
|
||||||
}
|
}
|
||||||
return r.Backend.Close()
|
|
||||||
|
// Since we're not responsible for _creating_ the connection to our DB
|
||||||
|
// backend, we also shouldn't close it. This should be handled
|
||||||
|
// externally as to not interfere with remote DB connections in case we
|
||||||
|
// need to open/close the store twice as happens in the password change
|
||||||
|
// case.
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateAndStoreNewRootKey creates a new random RootKeyLen-byte root key,
|
// generateAndStoreNewRootKey creates a new random RootKeyLen-byte root key,
|
||||||
@ -377,7 +385,7 @@ func (r *RootKeyStorage) ListMacaroonIDs(_ context.Context) ([][]byte, error) {
|
|||||||
|
|
||||||
// Read all the items in the bucket and append the keys, which are the
|
// Read all the items in the bucket and append the keys, which are the
|
||||||
// root key IDs we want.
|
// root key IDs we want.
|
||||||
err := kvdb.View(r, func(tx kvdb.RTx) error {
|
err := kvdb.View(r.Backend, func(tx kvdb.RTx) error {
|
||||||
|
|
||||||
// appendRootKey is a function closure that appends root key ID
|
// appendRootKey is a function closure that appends root key ID
|
||||||
// to rootKeySlice.
|
// to rootKeySlice.
|
||||||
@ -426,7 +434,7 @@ func (r *RootKeyStorage) DeleteMacaroonID(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rootKeyIDDeleted []byte
|
var rootKeyIDDeleted []byte
|
||||||
err := kvdb.Update(r, func(tx kvdb.RwTx) error {
|
err := kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
|
||||||
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
|
|
||||||
// Check the key can be found. If not, return nil.
|
// Check the key can be found. If not, return nil.
|
||||||
|
@ -54,6 +54,7 @@ func openTestStore(t *testing.T, tempDir string) (func(),
|
|||||||
|
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
_ = store.Close()
|
_ = store.Close()
|
||||||
|
_ = db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleanup, store
|
return cleanup, store
|
||||||
@ -108,6 +109,7 @@ func TestStore(t *testing.T) {
|
|||||||
require.Equal(t, macaroons.ErrAlreadyUnlocked, err)
|
require.Equal(t, macaroons.ErrAlreadyUnlocked, err)
|
||||||
|
|
||||||
_ = store.Close()
|
_ = store.Close()
|
||||||
|
_ = store.Backend.Close()
|
||||||
|
|
||||||
// Between here and the re-opening of the store, it's possible to get
|
// Between here and the re-opening of the store, it's possible to get
|
||||||
// a double-close, but that's not such a big deal since the tests will
|
// a double-close, but that's not such a big deal since the tests will
|
||||||
@ -206,6 +208,8 @@ func TestStoreChangePassword(t *testing.T) {
|
|||||||
// after closing.
|
// after closing.
|
||||||
err = store.Close()
|
err = store.Close()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
err = store.Backend.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = store.CreateUnlock(&newPw)
|
err = store.CreateUnlock(&newPw)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/aezeed"
|
"github.com/lightningnetwork/lnd/aezeed"
|
||||||
"github.com/lightningnetwork/lnd/chanbackup"
|
"github.com/lightningnetwork/lnd/chanbackup"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
||||||
@ -124,8 +125,6 @@ type UnlockerService struct {
|
|||||||
// the WalletUnlocker service.
|
// the WalletUnlocker service.
|
||||||
MacResponseChan chan []byte
|
MacResponseChan chan []byte
|
||||||
|
|
||||||
chainDir string
|
|
||||||
noFreelistSync bool
|
|
||||||
netParams *chaincfg.Params
|
netParams *chaincfg.Params
|
||||||
|
|
||||||
// macaroonFiles is the path to the three generated macaroons with
|
// macaroonFiles is the path to the three generated macaroons with
|
||||||
@ -133,21 +132,21 @@ type UnlockerService struct {
|
|||||||
// initialization of lnd.
|
// initialization of lnd.
|
||||||
macaroonFiles []string
|
macaroonFiles []string
|
||||||
|
|
||||||
// dbTimeout specifies the timeout value to use when opening the wallet
|
|
||||||
// database.
|
|
||||||
dbTimeout time.Duration
|
|
||||||
|
|
||||||
// resetWalletTransactions indicates that the wallet state should be
|
// resetWalletTransactions indicates that the wallet state should be
|
||||||
// reset on unlock to force a full chain rescan.
|
// reset on unlock to force a full chain rescan.
|
||||||
resetWalletTransactions bool
|
resetWalletTransactions bool
|
||||||
|
|
||||||
// LoaderOpts holds the functional options for the wallet loader.
|
// LoaderOpts holds the functional options for the wallet loader.
|
||||||
loaderOpts []btcwallet.LoaderOption
|
loaderOpts []btcwallet.LoaderOption
|
||||||
|
|
||||||
|
// macaroonDB is an instance of a database backend that stores all
|
||||||
|
// macaroon root keys. This will be nil on initialization and must be
|
||||||
|
// set using the SetMacaroonDB method as soon as it's available.
|
||||||
|
macaroonDB kvdb.Backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates and returns a new UnlockerService.
|
// New creates and returns a new UnlockerService.
|
||||||
func New(chainDir string, params *chaincfg.Params, noFreelistSync bool,
|
func New(params *chaincfg.Params, macaroonFiles []string,
|
||||||
macaroonFiles []string, dbTimeout time.Duration,
|
|
||||||
resetWalletTransactions bool,
|
resetWalletTransactions bool,
|
||||||
loaderOpts []btcwallet.LoaderOption) *UnlockerService {
|
loaderOpts []btcwallet.LoaderOption) *UnlockerService {
|
||||||
|
|
||||||
@ -158,11 +157,8 @@ func New(chainDir string, params *chaincfg.Params, noFreelistSync bool,
|
|||||||
// Make sure we buffer the channel is buffered so the main lnd
|
// Make sure we buffer the channel is buffered so the main lnd
|
||||||
// goroutine isn't blocking on writing to it.
|
// goroutine isn't blocking on writing to it.
|
||||||
MacResponseChan: make(chan []byte, 1),
|
MacResponseChan: make(chan []byte, 1),
|
||||||
chainDir: chainDir,
|
|
||||||
netParams: params,
|
netParams: params,
|
||||||
macaroonFiles: macaroonFiles,
|
macaroonFiles: macaroonFiles,
|
||||||
dbTimeout: dbTimeout,
|
|
||||||
noFreelistSync: noFreelistSync,
|
|
||||||
resetWalletTransactions: resetWalletTransactions,
|
resetWalletTransactions: resetWalletTransactions,
|
||||||
loaderOpts: loaderOpts,
|
loaderOpts: loaderOpts,
|
||||||
}
|
}
|
||||||
@ -174,6 +170,12 @@ func (u *UnlockerService) SetLoaderOpts(loaderOpts []btcwallet.LoaderOption) {
|
|||||||
u.loaderOpts = loaderOpts
|
u.loaderOpts = loaderOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMacaroonDB can be used to inject the macaroon database after the unlocker
|
||||||
|
// service has been hooked to the main RPC server.
|
||||||
|
func (u *UnlockerService) SetMacaroonDB(macaroonDB kvdb.Backend) {
|
||||||
|
u.macaroonDB = macaroonDB
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UnlockerService) newLoader(recoveryWindow uint32) (*wallet.Loader,
|
func (u *UnlockerService) newLoader(recoveryWindow uint32) (*wallet.Loader,
|
||||||
error) {
|
error) {
|
||||||
|
|
||||||
@ -607,9 +609,8 @@ func (u *UnlockerService) ChangePassword(ctx context.Context,
|
|||||||
// then close it again.
|
// then close it again.
|
||||||
// Attempt to open the macaroon DB, unlock it and then change
|
// Attempt to open the macaroon DB, unlock it and then change
|
||||||
// the passphrase.
|
// the passphrase.
|
||||||
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
|
|
||||||
macaroonService, err := macaroons.NewService(
|
macaroonService, err := macaroons.NewService(
|
||||||
netDir, "lnd", in.StatelessInit, u.dbTimeout,
|
u.macaroonDB, "lnd", in.StatelessInit,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -113,7 +113,7 @@ func openOrCreateTestMacStore(tempDir string, pw *[]byte,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
db, err := kvdb.Create(
|
db, err := kvdb.Create(
|
||||||
kvdb.BoltBackendName, path.Join(netDir, macaroons.DBFilename),
|
kvdb.BoltBackendName, path.Join(netDir, "macaroons.db"),
|
||||||
true, kvdb.DefaultDBTimeout,
|
true, kvdb.DefaultDBTimeout,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -154,8 +154,7 @@ func TestGenSeed(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
service := walletunlocker.New(
|
service := walletunlocker.New(
|
||||||
testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout,
|
testNetParams, nil, false, testLoaderOpts(testDir),
|
||||||
false, testLoaderOpts(testDir),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Now that the service has been created, we'll ask it to generate a
|
// Now that the service has been created, we'll ask it to generate a
|
||||||
@ -192,8 +191,7 @@ func TestGenSeedGenerateEntropy(t *testing.T) {
|
|||||||
_ = os.RemoveAll(testDir)
|
_ = os.RemoveAll(testDir)
|
||||||
}()
|
}()
|
||||||
service := walletunlocker.New(
|
service := walletunlocker.New(
|
||||||
testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout,
|
testNetParams, nil, false, testLoaderOpts(testDir),
|
||||||
false, testLoaderOpts(testDir),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Now that the service has been created, we'll ask it to generate a
|
// Now that the service has been created, we'll ask it to generate a
|
||||||
@ -229,8 +227,7 @@ func TestGenSeedInvalidEntropy(t *testing.T) {
|
|||||||
_ = os.RemoveAll(testDir)
|
_ = os.RemoveAll(testDir)
|
||||||
}()
|
}()
|
||||||
service := walletunlocker.New(
|
service := walletunlocker.New(
|
||||||
testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout,
|
testNetParams, nil, false, testLoaderOpts(testDir),
|
||||||
false, testLoaderOpts(testDir),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Now that the service has been created, we'll ask it to generate a
|
// Now that the service has been created, we'll ask it to generate a
|
||||||
@ -263,8 +260,7 @@ func TestInitWallet(t *testing.T) {
|
|||||||
|
|
||||||
// Create new UnlockerService.
|
// Create new UnlockerService.
|
||||||
service := walletunlocker.New(
|
service := walletunlocker.New(
|
||||||
testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout,
|
testNetParams, nil, false, testLoaderOpts(testDir),
|
||||||
false, testLoaderOpts(testDir),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Once we have the unlocker service created, we'll now instantiate a
|
// Once we have the unlocker service created, we'll now instantiate a
|
||||||
@ -352,8 +348,7 @@ func TestCreateWalletInvalidEntropy(t *testing.T) {
|
|||||||
|
|
||||||
// Create new UnlockerService.
|
// Create new UnlockerService.
|
||||||
service := walletunlocker.New(
|
service := walletunlocker.New(
|
||||||
testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout,
|
testNetParams, nil, false, testLoaderOpts(testDir),
|
||||||
false, testLoaderOpts(testDir),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// We'll attempt to init the wallet with an invalid cipher seed and
|
// We'll attempt to init the wallet with an invalid cipher seed and
|
||||||
@ -385,8 +380,7 @@ func TestUnlockWallet(t *testing.T) {
|
|||||||
// Create new UnlockerService that'll also drop the wallet's history on
|
// Create new UnlockerService that'll also drop the wallet's history on
|
||||||
// unlock.
|
// unlock.
|
||||||
service := walletunlocker.New(
|
service := walletunlocker.New(
|
||||||
testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout,
|
testNetParams, nil, true, testLoaderOpts(testDir),
|
||||||
true, testLoaderOpts(testDir),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@ -477,9 +471,9 @@ func TestChangeWalletPasswordNewRootkey(t *testing.T) {
|
|||||||
|
|
||||||
// Create a new UnlockerService with our temp files.
|
// Create a new UnlockerService with our temp files.
|
||||||
service := walletunlocker.New(
|
service := walletunlocker.New(
|
||||||
testDir, testNetParams, true, tempFiles, kvdb.DefaultDBTimeout,
|
testNetParams, tempFiles, false, testLoaderOpts(testDir),
|
||||||
false, testLoaderOpts(testDir),
|
|
||||||
)
|
)
|
||||||
|
service.SetMacaroonDB(store.Backend)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
newPassword := []byte("hunter2???")
|
newPassword := []byte("hunter2???")
|
||||||
@ -588,10 +582,11 @@ func TestChangeWalletPasswordStateless(t *testing.T) {
|
|||||||
|
|
||||||
// Create a new UnlockerService with our temp files.
|
// Create a new UnlockerService with our temp files.
|
||||||
service := walletunlocker.New(
|
service := walletunlocker.New(
|
||||||
testDir, testNetParams, true, []string{
|
testNetParams, []string{
|
||||||
tempMacFile, nonExistingFile,
|
tempMacFile, nonExistingFile,
|
||||||
}, kvdb.DefaultDBTimeout, false, testLoaderOpts(testDir),
|
}, false, testLoaderOpts(testDir),
|
||||||
)
|
)
|
||||||
|
service.SetMacaroonDB(store.Backend)
|
||||||
|
|
||||||
// Create a wallet we can try to unlock. We use the default password
|
// Create a wallet we can try to unlock. We use the default password
|
||||||
// so we can check that the unlocker service defaults to this when
|
// so we can check that the unlocker service defaults to this when
|
||||||
|
Loading…
x
Reference in New Issue
Block a user