mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
firewalldb+log: add main DB structure
This commit is contained in:
parent
69516ec675
commit
eaffa4a74a
4 changed files with 262 additions and 0 deletions
112
firewalldb/db.go
Normal file
112
firewalldb/db.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
package firewalldb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
const (
|
||||
// DBFilename is the default filename of the rules' database.
|
||||
DBFilename = "rules.db"
|
||||
|
||||
// dbFilePermission is the default permission the rules' database file
|
||||
// is created with.
|
||||
dbFilePermission = 0600
|
||||
|
||||
// DefaultRulesDBTimeout is the default maximum time we wait for the
|
||||
// db bbolt database to be opened. If the database is already
|
||||
// opened by another process, the unique lock cannot be obtained. With
|
||||
// the timeout we error out after the given time instead of just
|
||||
// blocking for forever.
|
||||
DefaultRulesDBTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
// byteOrder is the default byte order we'll use for serialization
|
||||
// within the database.
|
||||
byteOrder = binary.BigEndian
|
||||
)
|
||||
|
||||
// DB is a bolt-backed persistent store.
|
||||
type DB struct {
|
||||
*bbolt.DB
|
||||
}
|
||||
|
||||
// NewDB creates a new bolt database that can be found at the given directory.
|
||||
func NewDB(dir, fileName string) (*DB, error) {
|
||||
firstInit := false
|
||||
path := filepath.Join(dir, fileName)
|
||||
|
||||
// If the database file does not exist yet, create its directory.
|
||||
if !fileExists(path) {
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
firstInit = true
|
||||
}
|
||||
|
||||
db, err := initDB(path, firstInit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Attempt to sync the database's current version with the latest known
|
||||
// version available.
|
||||
if err := syncVersions(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DB{DB: db}, nil
|
||||
}
|
||||
|
||||
// fileExists reports whether the named file or directory exists.
|
||||
func fileExists(path string) bool {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// initDB initializes all the required top-level buckets for the database.
|
||||
func initDB(filepath string, firstInit bool) (*bbolt.DB, error) {
|
||||
db, err := bbolt.Open(filepath, dbFilePermission, &bbolt.Options{
|
||||
Timeout: DefaultRulesDBTimeout,
|
||||
})
|
||||
if err == bbolt.ErrTimeout {
|
||||
return nil, fmt.Errorf("error while trying to open %s: timed "+
|
||||
"out after %v when trying to obtain exclusive lock",
|
||||
filepath, DefaultRulesDBTimeout)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Update(func(tx *bbolt.Tx) error {
|
||||
if firstInit {
|
||||
metadataBucket, err := tx.CreateBucketIfNotExists(
|
||||
metadataBucketKey,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = setDBVersion(metadataBucket, latestDBVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
25
firewalldb/log.go
Normal file
25
firewalldb/log.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package firewalldb
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btclog"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
)
|
||||
|
||||
const Subsystem = "FWDB"
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
var log btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
UseLogger(build.NewSubLogger(Subsystem, nil))
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
// This should be used in preference to SetLogWriter if the caller is also
|
||||
// using btclog.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
||||
121
firewalldb/metadata.go
Normal file
121
firewalldb/metadata.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package firewalldb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
// migration is a function which takes a prior outdated version of the database
|
||||
// instance and mutates the key/bucket structure to arrive at a more up-to-date
|
||||
// version of the database.
|
||||
type migration func(tx *bbolt.Tx) error
|
||||
|
||||
var (
|
||||
// metadataBucketKey stores all the metadata concerning the state of the
|
||||
// database.
|
||||
metadataBucketKey = []byte("metadata")
|
||||
|
||||
// dbVersionKey is the key used for storing/retrieving the current
|
||||
// database version.
|
||||
dbVersionKey = []byte("version")
|
||||
|
||||
// ErrDBReversion is returned when detecting an attempt to revert to a
|
||||
// prior database version.
|
||||
ErrDBReversion = errors.New("cannot revert to prior version")
|
||||
|
||||
// dbVersions is storing all versions of database. If the current
|
||||
// version of the database doesn't match the latest version this list
|
||||
// will be used for retrieving all migration function that are need to
|
||||
// apply to the current db.
|
||||
dbVersions []migration
|
||||
|
||||
latestDBVersion = uint32(len(dbVersions))
|
||||
)
|
||||
|
||||
// getDBVersion retrieves the current database version.
|
||||
func getDBVersion(bucket *bbolt.Bucket) (uint32, error) {
|
||||
versionBytes := bucket.Get(dbVersionKey)
|
||||
if versionBytes == nil {
|
||||
return 0, errors.New("database version not found")
|
||||
}
|
||||
return byteOrder.Uint32(versionBytes), nil
|
||||
}
|
||||
|
||||
// setDBVersion updates the current database version.
|
||||
func setDBVersion(bucket *bbolt.Bucket, version uint32) error {
|
||||
var b [4]byte
|
||||
byteOrder.PutUint32(b[:], version)
|
||||
return bucket.Put(dbVersionKey, b[:])
|
||||
}
|
||||
|
||||
// getBucket retrieves the bucket with the given key.
|
||||
func getBucket(tx *bbolt.Tx, key []byte) (*bbolt.Bucket, error) {
|
||||
bucket := tx.Bucket(key)
|
||||
if bucket == nil {
|
||||
return nil, fmt.Errorf("bucket \"%v\" does not exist",
|
||||
string(key))
|
||||
}
|
||||
return bucket, nil
|
||||
}
|
||||
|
||||
// syncVersions function is used for safe db version synchronization. It
|
||||
// applies migration functions to the current database and recovers the
|
||||
// previous state of db if at least one error/panic appeared during migration.
|
||||
func syncVersions(db *bbolt.DB) error {
|
||||
var currentVersion uint32
|
||||
err := db.View(func(tx *bbolt.Tx) error {
|
||||
metadata, err := getBucket(tx, metadataBucketKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentVersion, err = getDBVersion(metadata)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Checking for schema update: latest_version=%v, "+
|
||||
"db_version=%v", latestDBVersion, currentVersion)
|
||||
|
||||
switch {
|
||||
// If the database reports a higher version that we are aware of, the
|
||||
// user is probably trying to revert to a prior version of lnd. We fail
|
||||
// here to prevent reversions and unintended corruption.
|
||||
case currentVersion > latestDBVersion:
|
||||
log.Errorf("Refusing to revert from db_version=%d to "+
|
||||
"lower version=%d", currentVersion,
|
||||
latestDBVersion)
|
||||
|
||||
return ErrDBReversion
|
||||
|
||||
// If the current database version matches the latest version number,
|
||||
// then we don't need to perform any migrations.
|
||||
case currentVersion == latestDBVersion:
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("Performing database schema migration")
|
||||
|
||||
// Otherwise, we execute the migrations serially within a single
|
||||
// database transaction to ensure the migration is atomic.
|
||||
return db.Update(func(tx *bbolt.Tx) error {
|
||||
for v := currentVersion; v < latestDBVersion; v++ {
|
||||
log.Infof("Applying migration #%v", v+1)
|
||||
|
||||
migration := dbVersions[v]
|
||||
if err := migration(tx); err != nil {
|
||||
log.Infof("Unable to apply migration #%v", v+1)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
metadata, err := getBucket(tx, metadataBucketKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setDBVersion(metadata, latestDBVersion)
|
||||
})
|
||||
}
|
||||
4
log.go
4
log.go
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/lightninglabs/lightning-node-connect/mailbox"
|
||||
"github.com/lightninglabs/lightning-terminal/accounts"
|
||||
"github.com/lightninglabs/lightning-terminal/firewall"
|
||||
"github.com/lightninglabs/lightning-terminal/firewalldb"
|
||||
mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware"
|
||||
"github.com/lightninglabs/lightning-terminal/session"
|
||||
"github.com/lightninglabs/loop/loopd"
|
||||
|
|
@ -69,6 +70,9 @@ func SetupLoggers(root *build.RotatingLogWriter, intercept signal.Interceptor) {
|
|||
lnd.AddSubLogger(
|
||||
root, firewall.Subsystem, intercept, firewall.UseLogger,
|
||||
)
|
||||
lnd.AddSubLogger(
|
||||
root, firewalldb.Subsystem, intercept, firewalldb.UseLogger,
|
||||
)
|
||||
|
||||
// Add daemon loggers to lnd's root logger.
|
||||
faraday.SetupLoggers(root, intercept)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue