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:
8 files changed
+244
-184
No files matched your search
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
@@ -14,11 +13,6 @@ import (
|
||||
"github.com/lightningnetwork/lnd/watchtower/blob"
|
||||
)
|
||||
|
||||
const (
|
||||
// clientDBName is the filename of client database.
|
||||
clientDBName = "wtclient.db"
|
||||
)
|
||||
|
||||
var (
|
||||
// cSessionKeyIndexBkt is a top-level bucket storing:
|
||||
// tower-id -> reserved-session-key-index (uint32).
|
||||
@@ -116,11 +110,43 @@ var (
|
||||
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
|
||||
// wtclient.
|
||||
type ClientDB struct {
|
||||
db kvdb.Backend
|
||||
dbPath string
|
||||
db kvdb.Backend
|
||||
}
|
||||
|
||||
// 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
|
||||
// with a version number higher that the latest version will fail to prevent
|
||||
// accidental reversion.
|
||||
func OpenClientDB(dbPath string, dbTimeout time.Duration) (*ClientDB, error) {
|
||||
bdb, firstInit, err := createDBIfNotExist(
|
||||
dbPath, clientDBName, dbTimeout,
|
||||
)
|
||||
func OpenClientDB(db kvdb.Backend) (*ClientDB, error) {
|
||||
firstInit, err := isFirstInit(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientDB := &ClientDB{
|
||||
db: bdb,
|
||||
dbPath: dbPath,
|
||||
db: db,
|
||||
}
|
||||
|
||||
err = initOrSyncVersions(clientDB, firstInit, clientDBVersions)
|
||||
if err != nil {
|
||||
bdb.Close()
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -156,7 +179,7 @@ func OpenClientDB(dbPath string, dbTimeout time.Duration) (*ClientDB, error) {
|
||||
// missing, this will trigger a ErrUninitializedDB error.
|
||||
err = kvdb.Update(clientDB.db, initClientDBBuckets, func() {})
|
||||
if err != nil {
|
||||
bdb.Close()
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -771,6 +771,7 @@ func checkAckedUpdates(t *testing.T, session *wtdb.ClientSession,
|
||||
// and the mock implementation. This ensures that all databases function
|
||||
// identically, especially in the negative paths.
|
||||
func TestClientDB(t *testing.T) {
|
||||
dbCfg := &kvdb.BoltConfig{DBTimeout: kvdb.DefaultDBTimeout}
|
||||
dbs := []struct {
|
||||
name string
|
||||
init clientDBInit
|
||||
@@ -784,9 +785,15 @@ func TestClientDB(t *testing.T) {
|
||||
err)
|
||||
}
|
||||
|
||||
db, err := wtdb.OpenClientDB(
|
||||
path, kvdb.DefaultDBTimeout,
|
||||
)
|
||||
bdb, err := wtdb.NewBoltBackendCreator(
|
||||
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 {
|
||||
os.RemoveAll(path)
|
||||
t.Fatalf("unable to open db: %v", err)
|
||||
@@ -809,18 +816,30 @@ func TestClientDB(t *testing.T) {
|
||||
err)
|
||||
}
|
||||
|
||||
db, err := wtdb.OpenClientDB(
|
||||
path, kvdb.DefaultDBTimeout,
|
||||
)
|
||||
bdb, err := wtdb.NewBoltBackendCreator(
|
||||
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 {
|
||||
os.RemoveAll(path)
|
||||
t.Fatalf("unable to open db: %v", err)
|
||||
}
|
||||
db.Close()
|
||||
|
||||
db, err = wtdb.OpenClientDB(
|
||||
path, kvdb.DefaultDBTimeout,
|
||||
)
|
||||
bdb, err = wtdb.NewBoltBackendCreator(
|
||||
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 {
|
||||
os.RemoveAll(path)
|
||||
t.Fatalf("unable to reopen db: %v", err)
|
||||
|
||||
@@ -3,18 +3,10 @@ package wtdb
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
)
|
||||
|
||||
const (
|
||||
// dbFilePermission requests read+write access to the db file.
|
||||
dbFilePermission = 0600
|
||||
)
|
||||
|
||||
var (
|
||||
// metadataBkt stores all the meta information concerning the state of
|
||||
// the database.
|
||||
@@ -35,67 +27,19 @@ var (
|
||||
byteOrder = binary.BigEndian
|
||||
)
|
||||
|
||||
// fileExists returns true if the file exists, and false otherwise.
|
||||
func fileExists(path string) bool {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
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,
|
||||
)
|
||||
// isFirstInit returns true if the given database has not yet been initialized,
|
||||
// e.g. no metadata bucket is present yet.
|
||||
func isFirstInit(db kvdb.Backend) (bool, error) {
|
||||
var metadataExists bool
|
||||
err := kvdb.View(db, func(tx kvdb.RTx) error {
|
||||
metadataExists = tx.ReadBucket(metadataBkt) != nil
|
||||
return nil
|
||||
}, func() {
|
||||
metadataExists = false
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return 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
|
||||
err = kvdb.View(bdb, func(tx kvdb.RTx) error {
|
||||
metadataExists = tx.ReadBucket(metadataBkt) != nil
|
||||
return nil
|
||||
}, func() {
|
||||
metadataExists = false
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if !metadataExists {
|
||||
firstInit = true
|
||||
}
|
||||
}
|
||||
|
||||
return bdb, firstInit, nil
|
||||
return !metadataExists, nil
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package wtdb
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
@@ -11,11 +10,6 @@ import (
|
||||
"github.com/lightningnetwork/lnd/watchtower/blob"
|
||||
)
|
||||
|
||||
const (
|
||||
// towerDBName is the filename of tower database.
|
||||
towerDBName = "watchtower.db"
|
||||
)
|
||||
|
||||
var (
|
||||
// sessionsBkt is a bucket containing all negotiated client sessions.
|
||||
// session id -> session
|
||||
@@ -56,8 +50,7 @@ var (
|
||||
// TowerDB is single database providing a persistent storage engine for the
|
||||
// wtserver and lookout subsystems.
|
||||
type TowerDB struct {
|
||||
db kvdb.Backend
|
||||
dbPath string
|
||||
db kvdb.Backend
|
||||
}
|
||||
|
||||
// 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
|
||||
// with a version number higher that the latest version will fail to prevent
|
||||
// accidental reversion.
|
||||
func OpenTowerDB(dbPath string, dbTimeout time.Duration) (*TowerDB, error) {
|
||||
bdb, firstInit, err := createDBIfNotExist(
|
||||
dbPath, towerDBName, dbTimeout,
|
||||
)
|
||||
func OpenTowerDB(db kvdb.Backend) (*TowerDB, error) {
|
||||
firstInit, err := isFirstInit(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
towerDB := &TowerDB{
|
||||
db: bdb,
|
||||
dbPath: dbPath,
|
||||
db: db,
|
||||
}
|
||||
|
||||
err = initOrSyncVersions(towerDB, firstInit, towerDBVersions)
|
||||
if err != nil {
|
||||
bdb.Close()
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -93,7 +83,7 @@ func OpenTowerDB(dbPath string, dbTimeout time.Duration) (*TowerDB, error) {
|
||||
// missing, this will trigger a ErrUninitializedDB error.
|
||||
err = kvdb.Update(towerDB.db, initTowerDBBuckets, func() {})
|
||||
if err != nil {
|
||||
bdb.Close()
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -630,6 +630,7 @@ var stateUpdateInvalidBlobSize = stateUpdateTest{
|
||||
}
|
||||
|
||||
func TestTowerDB(t *testing.T) {
|
||||
dbCfg := &kvdb.BoltConfig{DBTimeout: kvdb.DefaultDBTimeout}
|
||||
dbs := []struct {
|
||||
name string
|
||||
init dbInit
|
||||
@@ -643,9 +644,15 @@ func TestTowerDB(t *testing.T) {
|
||||
err)
|
||||
}
|
||||
|
||||
db, err := wtdb.OpenTowerDB(
|
||||
path, kvdb.DefaultDBTimeout,
|
||||
)
|
||||
bdb, err := wtdb.NewBoltBackendCreator(
|
||||
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 {
|
||||
os.RemoveAll(path)
|
||||
t.Fatalf("unable to open db: %v", err)
|
||||
@@ -668,9 +675,15 @@ func TestTowerDB(t *testing.T) {
|
||||
err)
|
||||
}
|
||||
|
||||
db, err := wtdb.OpenTowerDB(
|
||||
path, kvdb.DefaultDBTimeout,
|
||||
)
|
||||
bdb, err := wtdb.NewBoltBackendCreator(
|
||||
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 {
|
||||
os.RemoveAll(path)
|
||||
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
|
||||
// different path during open and that all
|
||||
// buckets remain initialized.
|
||||
db, err = wtdb.OpenTowerDB(
|
||||
path, kvdb.DefaultDBTimeout,
|
||||
)
|
||||
bdb, err = wtdb.NewBoltBackendCreator(
|
||||
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 {
|
||||
os.RemoveAll(path)
|
||||
t.Fatalf("unable to open db: %v", err)
|
||||
|
||||
Reference in new issue
Block a user