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 committed 2021-08-04 14:55:53 +02:00
1 parent 1e27f491c7
commit 75531455da
8 files changed
+244 -184

No files matched your search

+39 -16
View File
@@ -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
}
+28 -9
View File
@@ -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)
+12 -68
View File
@@ -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
}
+6 -16
View File
@@ -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
}
+28 -9
View File
@@ -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)