multi: make watchtower client/server DBs remote compatible

The final database that needs to be made remote compatible is the
watchtower server and client database.
They are handled a bit differently because both of them are not always
active, only when specifically turned on in the config.
This commit is contained in:
Oliver Gugger 2021-08-03 09:57:33 +02:00
parent 1e27f491c7
commit 75531455da
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
8 changed files with 244 additions and 184 deletions

View File

@ -13,6 +13,8 @@ const (
channelDBName = "channel.db" channelDBName = "channel.db"
macaroonDBName = "macaroons.db" macaroonDBName = "macaroons.db"
decayedLogDbName = "sphinxreplay.db" decayedLogDbName = "sphinxreplay.db"
towerClientDBName = "wtclient.db"
towerServerDBName = "watchtower.db"
BoltBackend = "bolt" BoltBackend = "bolt"
EtcdBackend = "etcd" EtcdBackend = "etcd"
@ -28,6 +30,14 @@ const (
// NSDecayedLogDB is the namespace name that we use for the sphinx // NSDecayedLogDB is the namespace name that we use for the sphinx
// replay a.k.a. decayed log DB. // replay a.k.a. decayed log DB.
NSDecayedLogDB = "decayedlogdb" NSDecayedLogDB = "decayedlogdb"
// NSTowerClientDB is the namespace name that we use for the watchtower
// client DB.
NSTowerClientDB = "towerclientdb"
// NSTowerServerDB is the namespace name that we use for the watchtower
// server DB.
NSTowerServerDB = "towerserverdb"
) )
// DB holds database configuration for LND. // DB holds database configuration for LND.
@ -117,6 +127,14 @@ type DatabaseBackends struct {
// data. // data.
DecayedLogDB kvdb.Backend DecayedLogDB kvdb.Backend
// TowerClientDB points to a database backend that stores the watchtower
// client data. This might be nil if the watchtower client is disabled.
TowerClientDB kvdb.Backend
// TowerServerDB points to a database backend that stores the watchtower
// server data. This might be nil if the watchtower server is disabled.
TowerServerDB 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
@ -128,7 +146,8 @@ 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, chanDBPath, func (db *DB) GetBackends(ctx context.Context, chanDBPath,
walletDBPath string) (*DatabaseBackends, error) { walletDBPath, towerServerDBPath string, towerClientEnabled,
towerServerEnabled bool) (*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
@ -164,6 +183,8 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
HeightHintDB: etcdBackend, HeightHintDB: etcdBackend,
MacaroonDB: etcdBackend, MacaroonDB: etcdBackend,
DecayedLogDB: etcdBackend, DecayedLogDB: etcdBackend,
TowerClientDB: etcdBackend,
TowerServerDB: etcdBackend,
Remote: true, Remote: true,
CloseFuncs: closeFuncs, CloseFuncs: closeFuncs,
}, nil }, nil
@ -209,6 +230,48 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
} }
closeFuncs[NSDecayedLogDB] = decayedLogBackend.Close closeFuncs[NSDecayedLogDB] = decayedLogBackend.Close
// The tower client is optional and might not be enabled by the user. We
// handle it being nil properly in the main server.
var towerClientBackend kvdb.Backend
if towerClientEnabled {
towerClientBackend, err = kvdb.GetBoltBackend(
&kvdb.BoltBackendConfig{
DBPath: chanDBPath,
DBFileName: towerClientDBName,
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 tower client "+
"DB: %v", err)
}
closeFuncs[NSTowerClientDB] = towerClientBackend.Close
}
// The tower server is optional and might not be enabled by the user. We
// handle it being nil properly in the main server.
var towerServerBackend kvdb.Backend
if towerServerEnabled {
towerServerBackend, err = kvdb.GetBoltBackend(
&kvdb.BoltBackendConfig{
DBPath: towerServerDBPath,
DBFileName: towerServerDBName,
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 tower server "+
"DB: %v", err)
}
closeFuncs[NSTowerServerDB] = towerServerBackend.Close
}
returnEarly = false returnEarly = false
return &DatabaseBackends{ return &DatabaseBackends{
GraphDB: boltBackend, GraphDB: boltBackend,
@ -216,6 +279,8 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
HeightHintDB: boltBackend, HeightHintDB: boltBackend,
MacaroonDB: macaroonBackend, MacaroonDB: macaroonBackend,
DecayedLogDB: decayedLogBackend, DecayedLogDB: decayedLogBackend,
TowerClientDB: towerClientBackend,
TowerServerDB: towerServerBackend,
CloseFuncs: closeFuncs, CloseFuncs: closeFuncs,
}, nil }, nil
} }

81
lnd.go
View File

@ -55,6 +55,7 @@ import (
"github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/tor"
"github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/walletunlocker"
"github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower"
"github.com/lightningnetwork/lnd/watchtower/wtclient"
"github.com/lightningnetwork/lnd/watchtower/wtdb" "github.com/lightningnetwork/lnd/watchtower/wtdb"
) )
@ -769,23 +770,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
"is proxying over Tor as well", cfg.Tor.StreamIsolation) "is proxying over Tor as well", cfg.Tor.StreamIsolation)
} }
// If the watchtower client should be active, open the client database.
// This is done here so that Close always executes when lndMain returns.
var towerClientDB *wtdb.ClientDB
if cfg.WtClient.Active {
var err error
towerClientDB, err = wtdb.OpenClientDB(
cfg.graphDatabaseDir(), cfg.DB.Bolt.DBTimeout,
)
if err != nil {
err := fmt.Errorf("unable to open watchtower client "+
"database: %v", err)
ltndLog.Error(err)
return err
}
defer towerClientDB.Close()
}
// If tor is active and either v2 or v3 onion services have been specified, // If tor is active and either v2 or v3 onion services have been specified,
// make a tor controller and pass it into both the watchtower server and // make a tor controller and pass it into both the watchtower server and
// the regular lnd server. // the regular lnd server.
@ -810,24 +794,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
var tower *watchtower.Standalone var tower *watchtower.Standalone
if cfg.Watchtower.Active { if cfg.Watchtower.Active {
// Segment the watchtower directory by chain and network.
towerDBDir := filepath.Join(
cfg.Watchtower.TowerDir,
cfg.registeredChains.PrimaryChain().String(),
lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
)
towerDB, err := wtdb.OpenTowerDB(
towerDBDir, cfg.DB.Bolt.DBTimeout,
)
if err != nil {
err := fmt.Errorf("unable to open watchtower "+
"database: %v", err)
ltndLog.Error(err)
return err
}
defer towerDB.Close()
towerKeyDesc, err := activeChainControl.KeyRing.DeriveKey( towerKeyDesc, err := activeChainControl.KeyRing.DeriveKey(
keychain.KeyLocator{ keychain.KeyLocator{
Family: keychain.KeyFamilyTowerID, Family: keychain.KeyFamilyTowerID,
@ -842,7 +808,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
wtCfg := &watchtower.Config{ wtCfg := &watchtower.Config{
BlockFetcher: activeChainControl.ChainIO, BlockFetcher: activeChainControl.ChainIO,
DB: towerDB, DB: dbs.towerServerDB,
EpochRegistrar: activeChainControl.ChainNotifier, EpochRegistrar: activeChainControl.ChainNotifier,
Net: cfg.net, Net: cfg.net,
NewAddress: func() (btcutil.Address, error) { NewAddress: func() (btcutil.Address, error) {
@ -894,9 +860,8 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
// Set up the core server which will listen for incoming peer // Set up the core server which will listen for incoming peer
// connections. // connections.
server, err := newServer( server, err := newServer(
cfg, cfg.Listeners, dbs, towerClientDB, cfg, cfg.Listeners, dbs, activeChainControl, &idKeyDesc,
activeChainControl, &idKeyDesc, walletInitParams.ChansToRestore, walletInitParams.ChansToRestore, chainedAcceptor, torController,
chainedAcceptor, torController,
) )
if err != nil { if err != nil {
err := fmt.Errorf("unable to create server: %v", err) err := fmt.Errorf("unable to create server: %v", err)
@ -1617,6 +1582,8 @@ type databaseInstances struct {
heightHintDB kvdb.Backend heightHintDB kvdb.Backend
macaroonDB kvdb.Backend macaroonDB kvdb.Backend
decayedLogDB kvdb.Backend decayedLogDB kvdb.Backend
towerClientDB wtclient.DB
towerServerDB watchtower.DB
} }
// initializeDatabases extracts the current databases that we'll use for normal // initializeDatabases extracts the current databases that we'll use for normal
@ -1637,7 +1604,11 @@ func initializeDatabases(ctx context.Context,
startOpenTime := time.Now() startOpenTime := time.Now()
databaseBackends, err := cfg.DB.GetBackends( databaseBackends, err := cfg.DB.GetBackends(
ctx, cfg.graphDatabaseDir(), cfg.networkDir, ctx, cfg.graphDatabaseDir(), cfg.networkDir, filepath.Join(
cfg.Watchtower.TowerDir,
cfg.registeredChains.PrimaryChain().String(),
lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
), cfg.WtClient.Active, cfg.Watchtower.Active,
) )
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("unable to obtain database "+ return nil, nil, fmt.Errorf("unable to obtain database "+
@ -1709,6 +1680,36 @@ func initializeDatabases(ctx context.Context,
// using the same struct (and DB backend) instance. // using the same struct (and DB backend) instance.
dbs.chanStateDB = dbs.graphDB dbs.chanStateDB = dbs.graphDB
// Wrap the watchtower client DB and make sure we clean up.
if cfg.WtClient.Active {
dbs.towerClientDB, err = wtdb.OpenClientDB(
databaseBackends.TowerClientDB,
)
if err != nil {
cleanUp()
err := fmt.Errorf("unable to open %s database: %v",
lncfg.NSTowerClientDB, err)
ltndLog.Error(err)
return nil, nil, err
}
}
// Wrap the watchtower server DB and make sure we clean up.
if cfg.Watchtower.Active {
dbs.towerServerDB, err = wtdb.OpenTowerDB(
databaseBackends.TowerServerDB,
)
if err != nil {
cleanUp()
err := fmt.Errorf("unable to open %s database: %v",
lncfg.NSTowerServerDB, err)
ltndLog.Error(err)
return nil, nil, err
}
}
openTime := time.Since(startOpenTime) openTime := time.Since(startOpenTime)
ltndLog.Infof("Database(s) now open (time_to_open=%v)!", openTime) ltndLog.Infof("Database(s) now open (time_to_open=%v)!", openTime)

View File

@ -352,8 +352,7 @@ func noiseDial(idKey keychain.SingleKeyECDH,
// newServer creates a new instance of the server which is to listen using the // newServer creates a new instance of the server which is to listen using the
// passed listener address. // passed listener address.
func newServer(cfg *Config, listenAddrs []net.Addr, func newServer(cfg *Config, listenAddrs []net.Addr,
dbs *databaseInstances, dbs *databaseInstances, cc *chainreg.ChainControl,
towerClientDB wtclient.DB, cc *chainreg.ChainControl,
nodeKeyDesc *keychain.KeyDescriptor, nodeKeyDesc *keychain.KeyDescriptor,
chansToRestore walletunlocker.ChannelsToRecover, chansToRestore walletunlocker.ChannelsToRecover,
chanPredicate chanacceptor.ChannelAcceptor, chanPredicate chanacceptor.ChannelAcceptor,
@ -1306,7 +1305,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
SecretKeyRing: s.cc.KeyRing, SecretKeyRing: s.cc.KeyRing,
Dial: cfg.net.Dial, Dial: cfg.net.Dial,
AuthDial: authDial, AuthDial: authDial,
DB: towerClientDB, DB: dbs.towerClientDB,
Policy: policy, Policy: policy,
ChainHash: *s.cfg.ActiveNetParams.GenesisHash, ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
MinBackoff: 10 * time.Second, MinBackoff: 10 * time.Second,
@ -1329,7 +1328,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
SecretKeyRing: s.cc.KeyRing, SecretKeyRing: s.cc.KeyRing,
Dial: cfg.net.Dial, Dial: cfg.net.Dial,
AuthDial: authDial, AuthDial: authDial,
DB: towerClientDB, DB: dbs.towerClientDB,
Policy: anchorPolicy, Policy: anchorPolicy,
ChainHash: *s.cfg.ActiveNetParams.GenesisHash, ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
MinBackoff: 10 * time.Second, MinBackoff: 10 * time.Second,

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"math" "math"
"net" "net"
"time"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
@ -14,11 +13,6 @@ import (
"github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/blob"
) )
const (
// clientDBName is the filename of client database.
clientDBName = "wtclient.db"
)
var ( var (
// cSessionKeyIndexBkt is a top-level bucket storing: // cSessionKeyIndexBkt is a top-level bucket storing:
// tower-id -> reserved-session-key-index (uint32). // tower-id -> reserved-session-key-index (uint32).
@ -116,11 +110,43 @@ var (
ErrLastTowerAddr = errors.New("cannot remove last tower address") ErrLastTowerAddr = errors.New("cannot remove last tower address")
) )
// NewBoltBackendCreator returns a function that creates a new bbolt backend for
// the watchtower database.
func NewBoltBackendCreator(active bool, dbPath,
dbFileName string) func(boltCfg *kvdb.BoltConfig) (kvdb.Backend, error) {
// If the watchtower client isn't active, we return a function that
// always returns a nil DB to make sure we don't create empty database
// files.
if !active {
return func(_ *kvdb.BoltConfig) (kvdb.Backend, error) {
return nil, nil
}
}
return func(boltCfg *kvdb.BoltConfig) (kvdb.Backend, error) {
cfg := &kvdb.BoltBackendConfig{
DBPath: dbPath,
DBFileName: dbFileName,
NoFreelistSync: !boltCfg.SyncFreelist,
AutoCompact: boltCfg.AutoCompact,
AutoCompactMinAge: boltCfg.AutoCompactMinAge,
DBTimeout: boltCfg.DBTimeout,
}
db, err := kvdb.GetBoltBackend(cfg)
if err != nil {
return nil, fmt.Errorf("could not open boltdb: %v", err)
}
return db, nil
}
}
// ClientDB is single database providing a persistent storage engine for the // ClientDB is single database providing a persistent storage engine for the
// wtclient. // wtclient.
type ClientDB struct { type ClientDB struct {
db kvdb.Backend db kvdb.Backend
dbPath string
} }
// OpenClientDB opens the client database given the path to the database's // OpenClientDB opens the client database given the path to the database's
@ -130,22 +156,19 @@ type ClientDB struct {
// migrations will be applied before returning. Any attempt to open a database // migrations will be applied before returning. Any attempt to open a database
// with a version number higher that the latest version will fail to prevent // with a version number higher that the latest version will fail to prevent
// accidental reversion. // accidental reversion.
func OpenClientDB(dbPath string, dbTimeout time.Duration) (*ClientDB, error) { func OpenClientDB(db kvdb.Backend) (*ClientDB, error) {
bdb, firstInit, err := createDBIfNotExist( firstInit, err := isFirstInit(db)
dbPath, clientDBName, dbTimeout,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
clientDB := &ClientDB{ clientDB := &ClientDB{
db: bdb, db: db,
dbPath: dbPath,
} }
err = initOrSyncVersions(clientDB, firstInit, clientDBVersions) err = initOrSyncVersions(clientDB, firstInit, clientDBVersions)
if err != nil { if err != nil {
bdb.Close() db.Close()
return nil, err return nil, err
} }
@ -156,7 +179,7 @@ func OpenClientDB(dbPath string, dbTimeout time.Duration) (*ClientDB, error) {
// missing, this will trigger a ErrUninitializedDB error. // missing, this will trigger a ErrUninitializedDB error.
err = kvdb.Update(clientDB.db, initClientDBBuckets, func() {}) err = kvdb.Update(clientDB.db, initClientDBBuckets, func() {})
if err != nil { if err != nil {
bdb.Close() db.Close()
return nil, err return nil, err
} }

View File

@ -771,6 +771,7 @@ func checkAckedUpdates(t *testing.T, session *wtdb.ClientSession,
// and the mock implementation. This ensures that all databases function // and the mock implementation. This ensures that all databases function
// identically, especially in the negative paths. // identically, especially in the negative paths.
func TestClientDB(t *testing.T) { func TestClientDB(t *testing.T) {
dbCfg := &kvdb.BoltConfig{DBTimeout: kvdb.DefaultDBTimeout}
dbs := []struct { dbs := []struct {
name string name string
init clientDBInit init clientDBInit
@ -784,9 +785,15 @@ func TestClientDB(t *testing.T) {
err) err)
} }
db, err := wtdb.OpenClientDB( bdb, err := wtdb.NewBoltBackendCreator(
path, kvdb.DefaultDBTimeout, true, path, "wtclient.db",
) )(dbCfg)
if err != nil {
os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err)
}
db, err := wtdb.OpenClientDB(bdb)
if err != nil { if err != nil {
os.RemoveAll(path) os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err) t.Fatalf("unable to open db: %v", err)
@ -809,18 +816,30 @@ func TestClientDB(t *testing.T) {
err) err)
} }
db, err := wtdb.OpenClientDB( bdb, err := wtdb.NewBoltBackendCreator(
path, kvdb.DefaultDBTimeout, true, path, "wtclient.db",
) )(dbCfg)
if err != nil {
os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err)
}
db, err := wtdb.OpenClientDB(bdb)
if err != nil { if err != nil {
os.RemoveAll(path) os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err) t.Fatalf("unable to open db: %v", err)
} }
db.Close() db.Close()
db, err = wtdb.OpenClientDB( bdb, err = wtdb.NewBoltBackendCreator(
path, kvdb.DefaultDBTimeout, true, path, "wtclient.db",
) )(dbCfg)
if err != nil {
os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err)
}
db, err = wtdb.OpenClientDB(bdb)
if err != nil { if err != nil {
os.RemoveAll(path) os.RemoveAll(path)
t.Fatalf("unable to reopen db: %v", err) t.Fatalf("unable to reopen db: %v", err)

View File

@ -3,18 +3,10 @@ package wtdb
import ( import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"os"
"path/filepath"
"time"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
) )
const (
// dbFilePermission requests read+write access to the db file.
dbFilePermission = 0600
)
var ( var (
// metadataBkt stores all the meta information concerning the state of // metadataBkt stores all the meta information concerning the state of
// the database. // the database.
@ -35,67 +27,19 @@ var (
byteOrder = binary.BigEndian byteOrder = binary.BigEndian
) )
// fileExists returns true if the file exists, and false otherwise. // isFirstInit returns true if the given database has not yet been initialized,
func fileExists(path string) bool { // e.g. no metadata bucket is present yet.
if _, err := os.Stat(path); err != nil { func isFirstInit(db kvdb.Backend) (bool, error) {
if os.IsNotExist(err) {
return false
}
}
return true
}
// createDBIfNotExist opens the boltdb database at dbPath/name, creating one if
// one doesn't exist. The boolean returned indicates if the database did not
// exist before, or if it has been created but no version metadata exists within
// it.
func createDBIfNotExist(dbPath, name string,
dbTimeout time.Duration) (kvdb.Backend, bool, error) {
path := filepath.Join(dbPath, name)
// If the database file doesn't exist, this indicates we much initialize
// a fresh database with the latest version.
firstInit := !fileExists(path)
if firstInit {
// Ensure all parent directories are initialized.
err := os.MkdirAll(dbPath, 0700)
if err != nil {
return nil, false, err
}
}
// Specify bbolt freelist options to reduce heap pressure in case the
// freelist grows to be very large.
bdb, err := kvdb.Create(
kvdb.BoltBackendName, path, true, dbTimeout,
)
if err != nil {
return nil, false, err
}
// If the file existed previously, we'll now check to see that the
// metadata bucket is properly initialized. It could be the case that
// the database was created, but we failed to actually populate any
// metadata. If the metadata bucket does not actually exist, we'll
// set firstInit to true so that we can treat is initialize the bucket.
if !firstInit {
var metadataExists bool var metadataExists bool
err = kvdb.View(bdb, func(tx kvdb.RTx) error { err := kvdb.View(db, func(tx kvdb.RTx) error {
metadataExists = tx.ReadBucket(metadataBkt) != nil metadataExists = tx.ReadBucket(metadataBkt) != nil
return nil return nil
}, func() { }, func() {
metadataExists = false metadataExists = false
}) })
if err != nil { if err != nil {
return nil, false, err return false, err
} }
if !metadataExists { return !metadataExists, nil
firstInit = true
}
}
return bdb, firstInit, nil
} }

View File

@ -3,7 +3,6 @@ package wtdb
import ( import (
"bytes" "bytes"
"errors" "errors"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
@ -11,11 +10,6 @@ import (
"github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/blob"
) )
const (
// towerDBName is the filename of tower database.
towerDBName = "watchtower.db"
)
var ( var (
// sessionsBkt is a bucket containing all negotiated client sessions. // sessionsBkt is a bucket containing all negotiated client sessions.
// session id -> session // session id -> session
@ -57,7 +51,6 @@ var (
// wtserver and lookout subsystems. // wtserver and lookout subsystems.
type TowerDB struct { type TowerDB struct {
db kvdb.Backend db kvdb.Backend
dbPath string
} }
// OpenTowerDB opens the tower database given the path to the database's // OpenTowerDB opens the tower database given the path to the database's
@ -67,22 +60,19 @@ type TowerDB struct {
// migrations will be applied before returning. Any attempt to open a database // migrations will be applied before returning. Any attempt to open a database
// with a version number higher that the latest version will fail to prevent // with a version number higher that the latest version will fail to prevent
// accidental reversion. // accidental reversion.
func OpenTowerDB(dbPath string, dbTimeout time.Duration) (*TowerDB, error) { func OpenTowerDB(db kvdb.Backend) (*TowerDB, error) {
bdb, firstInit, err := createDBIfNotExist( firstInit, err := isFirstInit(db)
dbPath, towerDBName, dbTimeout,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
towerDB := &TowerDB{ towerDB := &TowerDB{
db: bdb, db: db,
dbPath: dbPath,
} }
err = initOrSyncVersions(towerDB, firstInit, towerDBVersions) err = initOrSyncVersions(towerDB, firstInit, towerDBVersions)
if err != nil { if err != nil {
bdb.Close() db.Close()
return nil, err return nil, err
} }
@ -93,7 +83,7 @@ func OpenTowerDB(dbPath string, dbTimeout time.Duration) (*TowerDB, error) {
// missing, this will trigger a ErrUninitializedDB error. // missing, this will trigger a ErrUninitializedDB error.
err = kvdb.Update(towerDB.db, initTowerDBBuckets, func() {}) err = kvdb.Update(towerDB.db, initTowerDBBuckets, func() {})
if err != nil { if err != nil {
bdb.Close() db.Close()
return nil, err return nil, err
} }

View File

@ -630,6 +630,7 @@ var stateUpdateInvalidBlobSize = stateUpdateTest{
} }
func TestTowerDB(t *testing.T) { func TestTowerDB(t *testing.T) {
dbCfg := &kvdb.BoltConfig{DBTimeout: kvdb.DefaultDBTimeout}
dbs := []struct { dbs := []struct {
name string name string
init dbInit init dbInit
@ -643,9 +644,15 @@ func TestTowerDB(t *testing.T) {
err) err)
} }
db, err := wtdb.OpenTowerDB( bdb, err := wtdb.NewBoltBackendCreator(
path, kvdb.DefaultDBTimeout, true, path, "watchtower.db",
) )(dbCfg)
if err != nil {
os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err)
}
db, err := wtdb.OpenTowerDB(bdb)
if err != nil { if err != nil {
os.RemoveAll(path) os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err) t.Fatalf("unable to open db: %v", err)
@ -668,9 +675,15 @@ func TestTowerDB(t *testing.T) {
err) err)
} }
db, err := wtdb.OpenTowerDB( bdb, err := wtdb.NewBoltBackendCreator(
path, kvdb.DefaultDBTimeout, true, path, "watchtower.db",
) )(dbCfg)
if err != nil {
os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err)
}
db, err := wtdb.OpenTowerDB(bdb)
if err != nil { if err != nil {
os.RemoveAll(path) os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err) t.Fatalf("unable to open db: %v", err)
@ -680,9 +693,15 @@ func TestTowerDB(t *testing.T) {
// Open the db again, ensuring we test a // Open the db again, ensuring we test a
// different path during open and that all // different path during open and that all
// buckets remain initialized. // buckets remain initialized.
db, err = wtdb.OpenTowerDB( bdb, err = wtdb.NewBoltBackendCreator(
path, kvdb.DefaultDBTimeout, true, path, "watchtower.db",
) )(dbCfg)
if err != nil {
os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err)
}
db, err = wtdb.OpenTowerDB(bdb)
if err != nil { if err != nil {
os.RemoveAll(path) os.RemoveAll(path)
t.Fatalf("unable to open db: %v", err) t.Fatalf("unable to open db: %v", err)