lnd.xprv/chainregistry.go
Olaoluwa Osuntokun 8358349b2d
lnd: revert back to prior default wallet public passphrase
In this commit we ensure the behavior of lnd with the —noencryptwallet
command line option heaves as it did before user initiated wallet
encryption was implemented. We do this by modifying the
waitForWalletPassword method to instead return two pass phrases: one
public and one private. The default wallet public passphrase is then
restarted back to the value which was used stoically in the codebase
before the latest merged PR.
2017-10-19 19:53:28 -07:00

444 lines
13 KiB
Go

package main
import (
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
"github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/chainview"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/rpcclient"
"github.com/roasbeef/btcwallet/chain"
"github.com/roasbeef/btcwallet/walletdb"
)
// defaultBitcoinForwardingPolicy is the default forwarding policy used for
// Bitcoin channels.
var defaultBitcoinForwardingPolicy = htlcswitch.ForwardingPolicy{
MinHTLC: 0,
BaseFee: lnwire.NewMSatFromSatoshis(1),
FeeRate: 1,
TimeLockDelta: 144,
}
// defaultLitecoinForwardingPolicy is the default forwarding policy used for
// Litecoin channels.
var defaultLitecoinForwardingPolicy = htlcswitch.ForwardingPolicy{
MinHTLC: 0,
BaseFee: 1,
FeeRate: 1,
TimeLockDelta: 576,
}
// defaultChannelConstraints is the default set of channel constraints that are
// meant to be used when initially funding a channel.
//
// TODO(roasbeef): have one for both chains
var defaultChannelConstraints = channeldb.ChannelConstraints{
DustLimit: lnwallet.DefaultDustLimit(),
MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2,
}
// chainCode is an enum-like structure for keeping track of the chains
// currently supported within lnd.
type chainCode uint32
const (
// bitcoinChain is Bitcoin's testnet chain.
bitcoinChain chainCode = iota
// litecoinChain is Litecoin's testnet chain.
litecoinChain
)
// String returns a string representation of the target chainCode.
func (c chainCode) String() string {
switch c {
case bitcoinChain:
return "bitcoin"
case litecoinChain:
return "litecoin"
default:
return "kekcoin"
}
}
// chainControl couples the three primary interfaces lnd utilizes for a
// particular chain together. A single chainControl instance will exist for all
// the chains lnd is currently active on.
type chainControl struct {
chainIO lnwallet.BlockChainIO
feeEstimator lnwallet.FeeEstimator
signer lnwallet.Signer
msgSigner lnwallet.MessageSigner
chainNotifier chainntnfs.ChainNotifier
chainView chainview.FilteredChainView
wallet *lnwallet.LightningWallet
routingPolicy htlcswitch.ForwardingPolicy
}
// newChainControlFromConfig attempts to create a chainControl instance
// according to the parameters in the passed lnd configuration. Currently two
// branches of chainControl instances exist: one backed by a running btcd
// full-node, and the other backed by a running neutrino light client instance.
func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
privateWalletPw, publicWalletPw []byte) (*chainControl, func(), error) {
// Set the RPC config from the "home" chain. Multi-chain isn't yet
// active, so we'll restrict usage to a particular chain for now.
homeChainConfig := cfg.Bitcoin
if registeredChains.PrimaryChain() == litecoinChain {
homeChainConfig = cfg.Litecoin
}
ltndLog.Infof("Primary chain is set to: %v",
registeredChains.PrimaryChain())
cc := &chainControl{}
switch registeredChains.PrimaryChain() {
case bitcoinChain:
cc.routingPolicy = defaultBitcoinForwardingPolicy
cc.feeEstimator = lnwallet.StaticFeeEstimator{
FeeRate: 50,
}
case litecoinChain:
cc.routingPolicy = defaultLitecoinForwardingPolicy
cc.feeEstimator = lnwallet.StaticFeeEstimator{
FeeRate: 100,
}
default:
return nil, nil, fmt.Errorf("Default routing policy for "+
"chain %v is unknown", registeredChains.PrimaryChain())
}
walletConfig := &btcwallet.Config{
PrivatePass: privateWalletPw,
PublicPass: publicWalletPw,
DataDir: homeChainConfig.ChainDir,
NetParams: activeNetParams.Params,
FeeEstimator: cc.feeEstimator,
}
var (
err error
cleanUp func()
)
// If spv mode is active, then we'll be using a distinct set of
// chainControl interfaces that interface directly with the p2p network
// of the selected chain.
if cfg.NeutrinoMode.Active {
// First we'll open the database file for neutrino, creating
// the database if needed.
dbName := filepath.Join(cfg.DataDir, "neutrino.db")
nodeDatabase, err := walletdb.Create("bdb", dbName)
if err != nil {
return nil, nil, err
}
// With the database open, we can now create an instance of the
// neutrino light client. We pass in relevant configuration
// parameters required.
config := neutrino.Config{
DataDir: cfg.DataDir,
Database: nodeDatabase,
ChainParams: *activeNetParams.Params,
AddPeers: cfg.NeutrinoMode.AddPeers,
ConnectPeers: cfg.NeutrinoMode.ConnectPeers,
}
neutrino.WaitForMoreCFHeaders = time.Second * 1
neutrino.MaxPeers = 8
neutrino.BanDuration = 5 * time.Second
svc, err := neutrino.NewChainService(config)
if err != nil {
return nil, nil, fmt.Errorf("unable to create neutrino: %v", err)
}
svc.Start()
// Next we'll create the instances of the ChainNotifier and
// FilteredChainView interface which is backed by the neutrino
// light client.
cc.chainNotifier, err = neutrinonotify.New(svc)
if err != nil {
return nil, nil, err
}
cc.chainView, err = chainview.NewCfFilteredChainView(svc)
if err != nil {
return nil, nil, err
}
// Finally, we'll set the chain source for btcwallet, and
// create our clean up function which simply closes the
// database.
walletConfig.ChainSource = chain.NewNeutrinoClient(svc)
cleanUp = func() {
defer nodeDatabase.Close()
}
} else {
// Otherwise, we'll be speaking directly via RPC to a node.
//
// So first we'll load btcd/ltcd's TLS cert for the RPC
// connection. If a raw cert was specified in the config, then
// we'll set that directly. Otherwise, we attempt to read the
// cert from the path specified in the config.
var rpcCert []byte
if homeChainConfig.RawRPCCert != "" {
rpcCert, err = hex.DecodeString(homeChainConfig.RawRPCCert)
if err != nil {
return nil, nil, err
}
} else {
certFile, err := os.Open(homeChainConfig.RPCCert)
if err != nil {
return nil, nil, err
}
rpcCert, err = ioutil.ReadAll(certFile)
if err != nil {
return nil, nil, err
}
if err := certFile.Close(); err != nil {
return nil, nil, err
}
}
// If the specified host for the btcd/ltcd RPC server already
// has a port specified, then we use that directly. Otherwise,
// we assume the default port according to the selected chain
// parameters.
var btcdHost string
if strings.Contains(homeChainConfig.RPCHost, ":") {
btcdHost = homeChainConfig.RPCHost
} else {
btcdHost = fmt.Sprintf("%v:%v", homeChainConfig.RPCHost,
activeNetParams.rpcPort)
}
btcdUser := homeChainConfig.RPCUser
btcdPass := homeChainConfig.RPCPass
rpcConfig := &rpcclient.ConnConfig{
Host: btcdHost,
Endpoint: "ws",
User: btcdUser,
Pass: btcdPass,
Certificates: rpcCert,
DisableTLS: false,
DisableConnectOnNew: true,
DisableAutoReconnect: false,
}
cc.chainNotifier, err = btcdnotify.New(rpcConfig)
if err != nil {
return nil, nil, err
}
// Finally, we'll create an instance of the default chain view to be
// used within the routing layer.
cc.chainView, err = chainview.NewBtcdFilteredChainView(*rpcConfig)
if err != nil {
srvrLog.Errorf("unable to create chain view: %v", err)
return nil, nil, err
}
// Create a special websockets rpc client for btcd which will be used
// by the wallet for notifications, calls, etc.
chainRPC, err := chain.NewRPCClient(activeNetParams.Params, btcdHost,
btcdUser, btcdPass, rpcCert, false, 20)
if err != nil {
return nil, nil, err
}
walletConfig.ChainSource = chainRPC
}
wc, err := btcwallet.New(*walletConfig)
if err != nil {
fmt.Printf("unable to create wallet controller: %v\n", err)
return nil, nil, err
}
cc.msgSigner = wc
cc.signer = wc
cc.chainIO = wc
// Create, and start the lnwallet, which handles the core payment
// channel logic, and exposes control via proxy state machines.
walletCfg := lnwallet.Config{
Database: chanDB,
Notifier: cc.chainNotifier,
WalletController: wc,
Signer: cc.signer,
FeeEstimator: cc.feeEstimator,
ChainIO: cc.chainIO,
DefaultConstraints: defaultChannelConstraints,
NetParams: *activeNetParams.Params,
}
wallet, err := lnwallet.NewLightningWallet(walletCfg)
if err != nil {
fmt.Printf("unable to create wallet: %v\n", err)
return nil, nil, err
}
if err := wallet.Startup(); err != nil {
fmt.Printf("unable to start wallet: %v\n", err)
return nil, nil, err
}
ltndLog.Info("LightningWallet opened")
cc.wallet = wallet
return cc, cleanUp, nil
}
var (
// bitcoinGenesis is the genesis hash of Bitcoin's testnet chain.
bitcoinGenesis = chainhash.Hash([chainhash.HashSize]byte{
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
})
// litecoinGenesis is the genesis hash of Litecoin's testnet4 chain.
litecoinGenesis = chainhash.Hash([chainhash.HashSize]byte{
0xa0, 0x29, 0x3e, 0x4e, 0xeb, 0x3d, 0xa6, 0xe6,
0xf5, 0x6f, 0x81, 0xed, 0x59, 0x5f, 0x57, 0x88,
0x0d, 0x1a, 0x21, 0x56, 0x9e, 0x13, 0xee, 0xfd,
0xd9, 0x51, 0x28, 0x4b, 0x5a, 0x62, 0x66, 0x49,
})
// chainMap is a simple index that maps a chain's genesis hash to the
// chainCode enum for that chain.
chainMap = map[chainhash.Hash]chainCode{
bitcoinGenesis: bitcoinChain,
litecoinGenesis: litecoinChain,
}
// reverseChainMap is the inverse of the chainMap above: it maps the
// chain enum for a chain to its genesis hash.
reverseChainMap = map[chainCode]chainhash.Hash{
bitcoinChain: bitcoinGenesis,
litecoinChain: litecoinGenesis,
}
// chainDNSSeeds is a map of a chain's hash to the set of DNS seeds
// that will be use to bootstrap peers upon first startup.
//
// TODO(roasbeef): extend and collapse these and chainparams.go into
// struct like chaincfg.Params
chainDNSSeeds = map[chainhash.Hash][]string{
bitcoinGenesis: {
"nodes.lightning.directory",
//"lseed.bitcoinstats.com",
},
}
)
// chainRegistry keeps track of the current chains
type chainRegistry struct {
sync.RWMutex
activeChains map[chainCode]*chainControl
netParams map[chainCode]*bitcoinNetParams
primaryChain chainCode
}
// newChainRegistry creates a new chainRegistry.
func newChainRegistry() *chainRegistry {
return &chainRegistry{
activeChains: make(map[chainCode]*chainControl),
netParams: make(map[chainCode]*bitcoinNetParams),
}
}
// RegisterChain assigns an active chainControl instance to a target chain
// identified by its chainCode.
func (c *chainRegistry) RegisterChain(newChain chainCode, cc *chainControl) {
c.Lock()
c.activeChains[newChain] = cc
c.Unlock()
}
// LookupChain attempts to lookup an active chainControl instance for the
// target chain.
func (c *chainRegistry) LookupChain(targetChain chainCode) (*chainControl, bool) {
c.RLock()
cc, ok := c.activeChains[targetChain]
c.RUnlock()
return cc, ok
}
// LookupChainByHash attempts to look up an active chainControl which
// corresponds to the passed genesis hash.
func (c *chainRegistry) LookupChainByHash(chainHash chainhash.Hash) (*chainControl, bool) {
c.RLock()
defer c.RUnlock()
targetChain, ok := chainMap[chainHash]
if !ok {
return nil, ok
}
cc, ok := c.activeChains[targetChain]
return cc, ok
}
// RegisterPrimaryChain sets a target chain as the "home chain" for lnd.
func (c *chainRegistry) RegisterPrimaryChain(cc chainCode) {
c.Lock()
defer c.Unlock()
c.primaryChain = cc
}
// PrimaryChain returns the primary chain for this running lnd instance. The
// primary chain is considered the "home base" while the other registered
// chains are treated as secondary chains.
func (c *chainRegistry) PrimaryChain() chainCode {
c.RLock()
defer c.RUnlock()
return c.primaryChain
}
// ActiveChains returns the total number of active chains.
func (c *chainRegistry) ActiveChains() []chainCode {
c.RLock()
defer c.RUnlock()
chains := make([]chainCode, 0, len(c.activeChains))
for activeChain := range c.activeChains {
chains = append(chains, activeChain)
}
return chains
}
// NumActiveChains returns the total number of active chains.
func (c *chainRegistry) NumActiveChains() uint32 {
c.RLock()
defer c.RUnlock()
return uint32(len(c.activeChains))
}