mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-27 10:11:57 +01:00
346 lines
12 KiB
Go
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
|
|
}
|