lnd/config_builder.go
2021-10-08 12:08:25 +02:00

346 lines
12 KiB
Go

package lnd
import (
"context"
"fmt"
"path/filepath"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btclog"
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/watchtower"
"github.com/lightningnetwork/lnd/watchtower/wtclient"
"github.com/lightningnetwork/lnd/watchtower/wtdb"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
)
// GrpcRegistrar is an interface that must be satisfied by an external subserver
// that wants to be able to register its own gRPC server onto lnd's main
// grpc.Server instance.
type GrpcRegistrar interface {
// RegisterGrpcSubserver is called for each net.Listener on which lnd
// creates a grpc.Server instance. External subservers implementing this
// method can then register their own gRPC server structs to the main
// server instance.
RegisterGrpcSubserver(*grpc.Server) error
}
// RestRegistrar is an interface that must be satisfied by an external subserver
// that wants to be able to register its own REST mux onto lnd's main
// proxy.ServeMux instance.
type RestRegistrar interface {
// RegisterRestSubserver is called after lnd creates the main
// proxy.ServeMux instance. External subservers implementing this method
// can then register their own REST proxy stubs to the main server
// instance.
RegisterRestSubserver(context.Context, *proxy.ServeMux, string,
[]grpc.DialOption) error
}
// ExternalValidator is an interface that must be satisfied by an external
// macaroon validator.
type ExternalValidator interface {
macaroons.MacaroonValidator
// Permissions returns the permissions that the external validator is
// validating. It is a map between the full HTTP URI of each RPC and its
// required macaroon permissions. If multiple action/entity tuples are
// specified per URI, they are all required. See rpcserver.go for a list
// of valid action and entity values.
Permissions() map[string][]bakery.Op
}
// DatabaseBuilder is an interface that must be satisfied by the implementation
// that provides lnd's main database backend instances.
type DatabaseBuilder interface {
// BuildDatabase extracts the current databases that we'll use for
// normal operation in the daemon. A function closure that closes all
// opened databases is also returned.
BuildDatabase(ctx context.Context) (*DatabaseInstances, func(), error)
}
// ImplementationCfg is a struct that holds all configuration items for
// components that can be implemented outside lnd itself.
type ImplementationCfg struct {
// GrpcRegistrar is a type that can register additional gRPC subservers
// before the main gRPC server is started.
GrpcRegistrar
// RestRegistrar is a type that can register additional REST subservers
// before the main REST proxy is started.
RestRegistrar
// ExternalValidator is a type that can provide external macaroon
// validation.
ExternalValidator
// DatabaseBuilder is a type that can provide lnd's main database
// backend instances.
DatabaseBuilder
}
// DefaultWalletImpl is the default implementation of our normal, btcwallet
// backed configuration.
type DefaultWalletImpl struct {
}
// RegisterRestSubserver is called after lnd creates the main proxy.ServeMux
// instance. External subservers implementing this method can then register
// their own REST proxy stubs to the main server instance.
//
// NOTE: This is part of the GrpcRegistrar interface.
func (d *DefaultWalletImpl) RegisterRestSubserver(context.Context,
*proxy.ServeMux, string, []grpc.DialOption) error {
return nil
}
// RegisterGrpcSubserver is called for each net.Listener on which lnd creates a
// grpc.Server instance. External subservers implementing this method can then
// register their own gRPC server structs to the main server instance.
//
// NOTE: This is part of the GrpcRegistrar interface.
func (d *DefaultWalletImpl) RegisterGrpcSubserver(*grpc.Server) error {
return nil
}
// ValidateMacaroon extracts the macaroon from the context's gRPC metadata,
// checks its signature, makes sure all specified permissions for the called
// method are contained within and finally ensures all caveat conditions are
// met. A non-nil error is returned if any of the checks fail.
//
// NOTE: This is part of the ExternalValidator interface.
func (d *DefaultWalletImpl) ValidateMacaroon(ctx context.Context,
requiredPermissions []bakery.Op, fullMethod string) error {
// Because the default implementation does not return any permissions,
// we shouldn't be registered as an external validator at all and this
// should never be invoked.
return fmt.Errorf("default implementation does not support external " +
"macaroon validation")
}
// Permissions returns the permissions that the external validator is
// validating. It is a map between the full HTTP URI of each RPC and its
// required macaroon permissions. If multiple action/entity tuples are specified
// per URI, they are all required. See rpcserver.go for a list of valid action
// and entity values.
//
// NOTE: This is part of the ExternalValidator interface.
func (d *DefaultWalletImpl) Permissions() map[string][]bakery.Op {
return nil
}
// DatabaseInstances is a struct that holds all instances to the actual
// databases that are used in lnd.
type DatabaseInstances struct {
// GraphDB is the database that stores the channel graph used for path
// finding.
//
// NOTE/TODO: This currently _needs_ to be the same instance as the
// ChanStateDB below until the separation of the two databases is fully
// complete!
GraphDB *channeldb.DB
// ChanStateDB is the database that stores all of our node's channel
// state.
//
// NOTE/TODO: This currently _needs_ to be the same instance as the
// GraphDB above until the separation of the two databases is fully
// complete!
ChanStateDB *channeldb.DB
// HeightHintDB is the database that stores height hints for spends.
HeightHintDB kvdb.Backend
// MacaroonDB is the database that stores macaroon root keys.
MacaroonDB kvdb.Backend
// DecayedLogDB is the database that stores p2p related encryption
// information.
DecayedLogDB kvdb.Backend
// TowerClientDB is the database that stores the watchtower client's
// configuration.
TowerClientDB wtclient.DB
// TowerServerDB is the database that stores the watchtower server's
// configuration.
TowerServerDB watchtower.DB
// WalletDB is the configuration for loading the wallet database using
// the btcwallet's loader.
WalletDB btcwallet.LoaderOption
}
// DefaultDatabaseBuilder is a type that builds the default database backends
// for lnd, using the given configuration to decide what actual implementation
// to use.
type DefaultDatabaseBuilder struct {
cfg *Config
logger btclog.Logger
}
// NewDefaultDatabaseBuilder returns a new instance of the default database
// builder.
func NewDefaultDatabaseBuilder(cfg *Config,
logger btclog.Logger) *DefaultDatabaseBuilder {
return &DefaultDatabaseBuilder{
cfg: cfg,
logger: logger,
}
}
// BuildDatabase extracts the current databases that we'll use for normal
// operation in the daemon. A function closure that closes all opened databases
// is also returned.
func (d *DefaultDatabaseBuilder) BuildDatabase(
ctx context.Context) (*DatabaseInstances, func(), error) {
d.logger.Infof("Opening the main database, this might take a few " +
"minutes...")
cfg := d.cfg
if cfg.DB.Backend == lncfg.BoltBackend {
d.logger.Infof("Opening bbolt database, sync_freelist=%v, "+
"auto_compact=%v", !cfg.DB.Bolt.NoFreelistSync,
cfg.DB.Bolt.AutoCompact)
}
startOpenTime := time.Now()
databaseBackends, err := cfg.DB.GetBackends(
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 {
return nil, nil, fmt.Errorf("unable to obtain database "+
"backends: %v", err)
}
// With the full remote mode we made sure both the graph and channel
// state DB point to the same local or remote DB and the same namespace
// within that DB.
dbs := &DatabaseInstances{
HeightHintDB: databaseBackends.HeightHintDB,
MacaroonDB: databaseBackends.MacaroonDB,
DecayedLogDB: databaseBackends.DecayedLogDB,
WalletDB: databaseBackends.WalletDB,
}
cleanUp := func() {
// We can just close the returned close functions directly. Even
// if we decorate the channel DB with an additional struct, its
// close function still just points to the kvdb backend.
for name, closeFunc := range databaseBackends.CloseFuncs {
if err := closeFunc(); err != nil {
d.logger.Errorf("Error closing %s "+
"database: %v", name, err)
}
}
}
if databaseBackends.Remote {
d.logger.Infof("Using remote %v database! Creating "+
"graph and channel state DB instances", cfg.DB.Backend)
} else {
d.logger.Infof("Creating local graph and channel state DB " +
"instances")
}
dbOptions := []channeldb.OptionModifier{
channeldb.OptionSetRejectCacheSize(cfg.Caches.RejectCacheSize),
channeldb.OptionSetChannelCacheSize(cfg.Caches.ChannelCacheSize),
channeldb.OptionSetBatchCommitInterval(cfg.DB.BatchCommitInterval),
channeldb.OptionDryRunMigration(cfg.DryRunMigration),
}
// We want to pre-allocate the channel graph cache according to what we
// expect for mainnet to speed up memory allocation.
if cfg.ActiveNetParams.Name == chaincfg.MainNetParams.Name {
dbOptions = append(
dbOptions, channeldb.OptionSetPreAllocCacheNumNodes(
channeldb.DefaultPreAllocCacheNumNodes,
),
)
}
// Otherwise, we'll open two instances, one for the state we only need
// locally, and the other for things we want to ensure are replicated.
dbs.GraphDB, err = channeldb.CreateWithBackend(
databaseBackends.GraphDB, dbOptions...,
)
switch {
// Give the DB a chance to dry run the migration. Since we know that
// both the channel state and graph DBs are still always behind the same
// backend, we know this would be applied to both of those DBs.
case err == channeldb.ErrDryRunMigrationOK:
d.logger.Infof("Graph DB dry run migration successful")
return nil, nil, err
case err != nil:
cleanUp()
err := fmt.Errorf("unable to open graph DB: %v", err)
d.logger.Error(err)
return nil, nil, err
}
// For now, we don't _actually_ split the graph and channel state DBs on
// the code level. Since they both are based upon the *channeldb.DB
// struct it will require more refactoring to fully separate them. With
// the full remote mode we at least know for now that they both point to
// the same DB backend (and also namespace within that) so we only need
// to apply any migration once.
//
// TODO(guggero): Once the full separation of anything graph related
// from the channeldb.DB is complete, the decorated instance of the
// channel state DB should be created here individually instead of just
// using the same struct (and DB backend) instance.
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)
d.logger.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)
d.logger.Error(err)
return nil, nil, err
}
}
openTime := time.Since(startOpenTime)
d.logger.Infof("Database(s) now open (time_to_open=%v)!", openTime)
return dbs, cleanUp, nil
}