main/lntest: factor out node config options, add options for bitcoind

This commit factors out the btcd and ltcd options into their own sections
similar to neutrino, and adds a bitcoind section as well. Now, you specify
node options similarly to:

--ltcd.rpchost=...
or
--btcd.rpcuser=...
or
--bitcoind.zmqpath=...

For Bitcoin, you specify an alternate back-end to btcd as follows:

--bitcoin.node=bitcoind
or
--bitcoin.node=neutrino

You can also specify the default option:
--bitcoin.node=btcd

For Litecoin, only `btcd` mode is valid, and corresponds to the `ltcd`
section. For example:

--litecoin.node=btcd
--ltcd.rpchost=...

The new code also attempts to read the correct options and auth info
from bitcoin.conf just as it does from btcd.conf/ltcd.conf.
This commit is contained in:
Alex 2017-12-21 22:23:24 -07:00 committed by Olaoluwa Osuntokun
parent 187f59556a
commit 9a02884d0b
7 changed files with 398 additions and 115 deletions

View File

@ -4,14 +4,17 @@ import (
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify"
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
"github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
"github.com/lightningnetwork/lnd/channeldb"
@ -134,15 +137,17 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
}
var (
err error
cleanUp func()
rawRPCConn *chain.RPCClient
err error
cleanUp func()
btcdConn *chain.RPCClient
bitcoindConn *chain.BitcoindClient
)
// 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 {
switch homeChainConfig.Node {
case "neutrino":
// First we'll open the database file for neutrino, creating
// the database if needed.
dbName := filepath.Join(cfg.DataDir, "neutrino.db")
@ -189,21 +194,121 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
cleanUp = func() {
defer nodeDatabase.Close()
}
} else {
case "bitcoind":
// Otherwise, we'll be speaking directly via RPC and ZMQ to a
// bitcoind node. 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 bitcoindHost string
if strings.Contains(cfg.BitcoindMode.RPCHost, ":") {
bitcoindHost = cfg.BitcoindMode.RPCHost
} else {
// The RPC ports specified in chainparams.go assume
// btcd, which picks a different port so that btcwallet
// can use the same RPC port as bitcoind. We convert
// this back to the btcwallet/bitcoind port.
rpcPort, err := strconv.Atoi(activeNetParams.rpcPort)
if err != nil {
return nil, nil, err
}
rpcPort -= 2
bitcoindHost = fmt.Sprintf("%v:%d",
cfg.BitcoindMode.RPCHost, rpcPort)
if cfg.Bitcoin.RegTest {
conn, err := net.Dial("tcp", bitcoindHost)
if err != nil || conn == nil {
rpcPort = 18443
bitcoindHost = fmt.Sprintf("%v:%d",
cfg.BitcoindMode.RPCHost,
rpcPort)
} else {
conn.Close()
}
}
}
bitcoindUser := cfg.BitcoindMode.RPCUser
bitcoindPass := cfg.BitcoindMode.RPCPass
rpcConfig := &rpcclient.ConnConfig{
Host: bitcoindHost,
User: bitcoindUser,
Pass: bitcoindPass,
DisableConnectOnNew: true,
DisableAutoReconnect: false,
DisableTLS: true,
HTTPPostMode: true,
}
cc.chainNotifier, err = bitcoindnotify.New(rpcConfig,
cfg.BitcoindMode.ZMQPath, *activeNetParams.Params)
if err != nil {
return nil, nil, err
}
// Next, we'll create an instance of the bitcoind chain view to
// be used within the routing layer.
cc.chainView, err = chainview.NewBitcoindFilteredChainView(
*rpcConfig, cfg.BitcoindMode.ZMQPath,
*activeNetParams.Params)
if err != nil {
srvrLog.Errorf("unable to create chain view: %v", err)
return nil, nil, err
}
// Create a special rpc+ZMQ client for bitcoind which will be
// used by the wallet for notifications, calls, etc.
bitcoindConn, err = chain.NewBitcoindClient(
activeNetParams.Params, bitcoindHost, bitcoindUser,
bitcoindPass, cfg.BitcoindMode.ZMQPath,
time.Millisecond*100)
if err != nil {
return nil, nil, err
}
walletConfig.ChainSource = bitcoindConn
// If we're not in regtest mode, then we'll attempt to use a
// proper fee estimator for testnet.
if !cfg.Bitcoin.RegTest {
ltndLog.Infof("Initializing bitcoind backed fee estimator")
// Finally, we'll re-initialize the fee estimator, as
// if we're using bitcoind as a backend, then we can
// use live fee estimates, rather than a statically
// coded value.
fallBackFeeRate := btcutil.Amount(25)
cc.feeEstimator, err = lnwallet.NewBitcoindFeeEstimator(
*rpcConfig, fallBackFeeRate,
)
if err != nil {
return nil, nil, err
}
if err := cc.feeEstimator.Start(); err != nil {
return nil, nil, err
}
}
case "btcd":
// 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 btcdMode *btcdConfig
switch {
case cfg.Bitcoin.Active:
btcdMode = cfg.BtcdMode
case cfg.Litecoin.Active:
btcdMode = cfg.LtcdMode
}
var rpcCert []byte
if homeChainConfig.RawRPCCert != "" {
rpcCert, err = hex.DecodeString(homeChainConfig.RawRPCCert)
if btcdMode.RawRPCCert != "" {
rpcCert, err = hex.DecodeString(btcdMode.RawRPCCert)
if err != nil {
return nil, nil, err
}
} else {
certFile, err := os.Open(homeChainConfig.RPCCert)
certFile, err := os.Open(btcdMode.RPCCert)
if err != nil {
return nil, nil, err
}
@ -221,15 +326,15 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// we assume the default port according to the selected chain
// parameters.
var btcdHost string
if strings.Contains(homeChainConfig.RPCHost, ":") {
btcdHost = homeChainConfig.RPCHost
if strings.Contains(btcdMode.RPCHost, ":") {
btcdHost = btcdMode.RPCHost
} else {
btcdHost = fmt.Sprintf("%v:%v", homeChainConfig.RPCHost,
btcdHost = fmt.Sprintf("%v:%v", btcdMode.RPCHost,
activeNetParams.rpcPort)
}
btcdUser := homeChainConfig.RPCUser
btcdPass := homeChainConfig.RPCPass
btcdUser := btcdMode.RPCUser
btcdPass := btcdMode.RPCPass
rpcConfig := &rpcclient.ConnConfig{
Host: btcdHost,
Endpoint: "ws",
@ -262,7 +367,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
}
walletConfig.ChainSource = chainRPC
rawRPCConn = chainRPC
btcdConn = chainRPC
// If we're not in simnet or regtest mode, then we'll attempt
// to use a proper fee estimator for testnet.
@ -286,6 +391,9 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
return nil, nil, err
}
}
default:
return nil, nil, fmt.Errorf("unknown node type: %s",
homeChainConfig.Node)
}
wc, err := btcwallet.New(*walletConfig)
@ -327,7 +435,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// As a final check, if we're using the RPC backend, we'll ensure that
// the btcd node has the txindex set. Atm, this is required in order to
// properly perform historical confirmation+spend dispatches.
if !cfg.NeutrinoMode.Active {
if homeChainConfig.Node != "neutrino" {
// In order to check to see if we have the txindex up to date
// and active, we'll try to fetch the first transaction in the
// latest block via the index. If this doesn't succeed, then we
@ -344,12 +452,18 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
}
firstTxHash := bestBlock.Transactions[0].TxHash()
_, err = rawRPCConn.GetRawTransaction(&firstTxHash)
switch homeChainConfig.Node {
case "btcd":
_, err = btcdConn.GetRawTransaction(&firstTxHash)
case "bitcoind":
_, err = bitcoindConn.GetRawTransactionVerbose(&firstTxHash)
}
if err != nil {
// If the node doesn't have the txindex set, then we'll
// halt startup, as we can't proceed in this state.
return nil, nil, fmt.Errorf("btcd detected to not " +
"have --txindex active, cannot proceed")
return nil, nil, fmt.Errorf("%s detected to not "+
"have --txindex active, cannot proceed",
homeChainConfig.Node)
}
}

328
config.go
View File

@ -9,6 +9,7 @@ import (
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"regexp"
"sort"
@ -16,7 +17,7 @@ import (
"strings"
"time"
flags "github.com/btcsuite/go-flags"
flags "github.com/jessevdk/go-flags"
"github.com/lightningnetwork/lnd/brontide"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/btcec"
@ -72,17 +73,15 @@ var (
ltcdHomeDir = btcutil.AppDataDir("ltcd", false)
defaultLtcdRPCCertFile = filepath.Join(ltcdHomeDir, "rpc.cert")
bitcoindHomeDir = btcutil.AppDataDir("bitcoin", false)
)
type chainConfig struct {
Active bool `long:"active" description:"If the chain should be active or not."`
ChainDir string `long:"chaindir" description:"The directory to store the chain's data within."`
RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."`
RPCUser string `long:"rpcuser" description:"Username for RPC connections"`
RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"`
RPCCert string `long:"rpccert" description:"File containing the daemon's certificate file"`
RawRPCCert string `long:"rawrpccert" description:"The raw bytes of the daemon's PEM-encoded certificate chain which will be used to authenticate the RPC connection."`
Node string `long:"node" description:"The blockchain interface to use." choice:"btcd" choice:"bitcoind" choice:"neutrino"`
TestNet3 bool `long:"testnet" description:"Use the test network"`
SimNet bool `long:"simnet" description:"Use the simulation test network"`
@ -97,7 +96,6 @@ type chainConfig struct {
}
type neutrinoConfig struct {
Active bool `long:"active" description:"If SPV mode should be active or not."`
AddPeers []string `short:"a" long:"addpeer" description:"Add a peer to connect with at startup"`
ConnectPeers []string `long:"connect" description:"Connect only to the specified peers at startup"`
MaxPeers int `long:"maxpeers" description:"Max number of inbound and outbound peers"`
@ -105,6 +103,21 @@ type neutrinoConfig struct {
BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."`
}
type btcdConfig struct {
RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."`
RPCUser string `long:"rpcuser" description:"Username for RPC connections"`
RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"`
RPCCert string `long:"rpccert" description:"File containing the daemon's certificate file"`
RawRPCCert string `long:"rawrpccert" description:"The raw bytes of the daemon's PEM-encoded certificate chain which will be used to authenticate the RPC connection."`
}
type bitcoindConfig struct {
RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."`
RPCUser string `long:"rpcuser" description:"Username for RPC connections"`
RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"`
ZMQPath string `long:"zmqpath" description:"The path to the ZMQ socket providing at least raw blocks. Raw transactions can be handled as well."`
}
type autoPilotConfig struct {
// TODO(roasbeef): add
Active bool `long:"active" description:"If the autopilot agent should be active or not."`
@ -144,11 +157,14 @@ type config struct {
HodlHTLC bool `long:"hodlhtlc" description:"Activate the hodl HTLC mode. With hodl HTLC mode, all incoming HTLCs will be accepted by the receiving node, but no attempt will be made to settle the payment with the sender."`
MaxPendingChannels int `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."`
Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"`
Bitcoin *chainConfig `group:"Bitcoin" namespace:"bitcoin"`
Bitcoin *chainConfig `group:"Bitcoin" namespace:"bitcoin"`
BtcdMode *btcdConfig `group:"btcd" namespace:"btcd"`
BitcoindMode *bitcoindConfig `group:"bitcoind" namespace:"bitcoind"`
NeutrinoMode *neutrinoConfig `group:"neutrino" namespace:"neutrino"`
Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"`
LtcdMode *btcdConfig `group:"ltcd" namespace:"ltcd"`
Autopilot *autoPilotConfig `group:"autopilot" namespace:"autopilot"`
NoNetBootstrap bool `long:"nobootstrap" description:"If true, then automatic network bootstrapping will not be attempted."`
@ -168,35 +184,44 @@ type config struct {
// 4) Parse CLI options and overwrite/add any specified options
func loadConfig() (*config, error) {
defaultCfg := config{
ConfigFile: defaultConfigFile,
DataDir: defaultDataDir,
DebugLevel: defaultLogLevel,
TLSCertPath: defaultTLSCertPath,
TLSKeyPath: defaultTLSKeyPath,
AdminMacPath: defaultAdminMacPath,
ReadMacPath: defaultReadMacPath,
LogDir: defaultLogDir,
PeerPort: defaultPeerPort,
RPCPort: defaultRPCPort,
RESTPort: defaultRESTPort,
MaxPendingChannels: defaultMaxPendingChannels,
NoEncryptWallet: defaultNoEncryptWallet,
ConfigFile: defaultConfigFile,
DataDir: defaultDataDir,
DebugLevel: defaultLogLevel,
TLSCertPath: defaultTLSCertPath,
TLSKeyPath: defaultTLSKeyPath,
AdminMacPath: defaultAdminMacPath,
ReadMacPath: defaultReadMacPath,
LogDir: defaultLogDir,
PeerPort: defaultPeerPort,
RPCPort: defaultRPCPort,
RESTPort: defaultRESTPort,
Bitcoin: &chainConfig{
RPCHost: defaultRPCHost,
RPCCert: defaultBtcdRPCCertFile,
MinHTLC: defaultBitcoinMinHTLCMSat,
BaseFee: defaultBitcoinBaseFeeMSat,
FeeRate: defaultBitcoinFeeRate,
TimeLockDelta: defaultBitcoinTimeLockDelta,
Node: "btcd",
},
BtcdMode: &btcdConfig{
RPCHost: defaultRPCHost,
RPCCert: defaultBtcdRPCCertFile,
},
BitcoindMode: &bitcoindConfig{
RPCHost: defaultRPCHost,
},
Litecoin: &chainConfig{
RPCHost: defaultRPCHost,
RPCCert: defaultLtcdRPCCertFile,
MinHTLC: defaultLitecoinMinHTLCMSat,
BaseFee: defaultLitecoinBaseFeeMSat,
FeeRate: defaultLitecoinFeeRate,
TimeLockDelta: defaultLitecoinTimeLockDelta,
Node: "btcd",
},
LtcdMode: &btcdConfig{
RPCHost: defaultRPCHost,
RPCCert: defaultLtcdRPCCertFile,
},
MaxPendingChannels: defaultMaxPendingChannels,
NoEncryptWallet: defaultNoEncryptWallet,
Autopilot: &autoPilotConfig{
MaxChannels: 5,
Allocation: 0.6,
@ -252,28 +277,18 @@ func loadConfig() (*config, error) {
return nil, err
}
switch {
// At this moment, multiple active chains are not supported.
if cfg.Litecoin.Active && cfg.Bitcoin.Active {
case cfg.Litecoin.Active && cfg.Bitcoin.Active:
str := "%s: Currently both Bitcoin and Litecoin cannot be " +
"active together"
err := fmt.Errorf(str, funcName)
return nil, err
}
switch {
// The SPV mode implemented currently doesn't support Litecoin, so the
// two modes are incompatible.
case cfg.NeutrinoMode.Active && cfg.Litecoin.Active:
str := "%s: The light client mode currently supported does " +
"not yet support execution on the Litecoin network"
err := fmt.Errorf(str, funcName)
return nil, err
return nil, fmt.Errorf(str, funcName)
// Either Bitcoin must be active, or Litecoin must be active.
// Otherwise, we don't know which chain we're on.
case !cfg.Bitcoin.Active && !cfg.Litecoin.Active:
return nil, fmt.Errorf("either bitcoin.active or " +
"litecoin.active must be set to 1 (true)")
return nil, fmt.Errorf("%s: either bitcoin.active or "+
"litecoin.active must be set to 1 (true)", funcName)
case cfg.Litecoin.Active:
if cfg.Litecoin.SimNet {
@ -286,25 +301,26 @@ func loadConfig() (*config, error) {
minTimeLockDelta)
}
if cfg.Litecoin.Node != "btcd" {
str := "%s: only ltcd (`btcd`) mode supported for litecoin at this time"
return nil, fmt.Errorf(str, funcName)
}
// The litecoin chain is the current active chain. However
// throughout the codebase we required chiancfg.Params. So as a
// throughout the codebase we required chaincfg.Params. So as a
// temporary hack, we'll mutate the default net params for
// bitcoin with the litecoin specific information.
paramCopy := bitcoinTestNetParams
applyLitecoinParams(&paramCopy)
activeNetParams = paramCopy
if !cfg.NeutrinoMode.Active {
// Attempt to parse out the RPC credentials for the
// litecoin chain if the information wasn't specified
err := parseRPCParams(cfg.Litecoin, litecoinChain, funcName)
if err != nil {
err := fmt.Errorf("unable to load RPC credentials for "+
"ltcd: %v", err)
return nil, err
}
err := parseRPCParams(cfg.Litecoin, cfg.LtcdMode, litecoinChain,
funcName)
if err != nil {
err := fmt.Errorf("unable to load RPC credentials for "+
"ltcd: %v", err)
return nil, err
}
cfg.Litecoin.ChainDir = filepath.Join(cfg.DataDir, litecoinChain.String())
// Finally we'll register the litecoin chain as our current
@ -340,15 +356,29 @@ func loadConfig() (*config, error) {
minTimeLockDelta)
}
if !cfg.NeutrinoMode.Active {
// If needed, we'll attempt to automatically configure
// the RPC control plan for the target btcd node.
err := parseRPCParams(cfg.Bitcoin, bitcoinChain, funcName)
switch cfg.Bitcoin.Node {
case "btcd":
err := parseRPCParams(cfg.Bitcoin, cfg.BtcdMode,
bitcoinChain, funcName)
if err != nil {
err := fmt.Errorf("unable to load RPC credentials for "+
"btcd: %v", err)
err := fmt.Errorf("unable to load RPC "+
"credentials for btcd: %v", err)
return nil, err
}
case "bitcoind":
if cfg.Bitcoin.SimNet {
return nil, fmt.Errorf("%s: bitcoind does not "+
"support simnet", funcName)
}
err := parseRPCParams(cfg.Bitcoin, cfg.BitcoindMode,
bitcoinChain, funcName)
if err != nil {
err := fmt.Errorf("unable to load RPC "+
"credentials for bitcoind: %v", err)
return nil, err
}
case "neutrino":
// No need to get RPC parameters.
}
cfg.Bitcoin.ChainDir = filepath.Join(cfg.DataDir, bitcoinChain.String())
@ -539,12 +569,20 @@ func noiseDial(idPriv *btcec.PrivateKey) func(net.Addr) (net.Conn, error) {
}
}
func parseRPCParams(cConfig *chainConfig, net chainCode, funcName string) error {
// If the rpcuser and rpcpass parameters aren't set, then we'll attempt
// to automatically obtain the proper credentials for btcd and set
// them within the configuration.
if cConfig.RPCUser != "" || cConfig.RPCPass != "" {
return nil
func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode,
funcName string) error {
// If the configuration has already set the RPCUser and RPCPass, and
// if we're either not using bitcoind mode or the ZMQ path is already
// specified, we can return.
switch conf := nodeConfig.(type) {
case *btcdConfig:
if conf.RPCUser != "" || conf.RPCPass != "" {
return nil
}
case *bitcoindConfig:
if conf.RPCUser != "" || conf.RPCPass != "" || conf.ZMQPath != "" {
return nil
}
}
// If we're in simnet mode, then the running btcd instance won't read
@ -556,33 +594,62 @@ func parseRPCParams(cConfig *chainConfig, net chainCode, funcName string) error
return fmt.Errorf(str, funcName)
}
daemonName := "btcd"
if net == litecoinChain {
daemonName = "ltcd"
var daemonName, homeDir, confFile string
switch net {
case bitcoinChain:
switch cConfig.Node {
case "btcd":
daemonName = "btcd"
homeDir = btcdHomeDir
confFile = "btcd"
case "bitcoind":
daemonName = "bitcoind"
homeDir = bitcoindHomeDir
confFile = "bitcoin"
}
case litecoinChain:
switch cConfig.Node {
case "btcd":
daemonName = "ltcd"
homeDir = ltcdHomeDir
confFile = "ltcd"
case "bitcoind":
return fmt.Errorf("bitcoind mode doesn't work with Litecoin yet")
}
}
fmt.Println("Attempting automatic RPC configuration to " + daemonName)
homeDir := btcdHomeDir
if net == litecoinChain {
homeDir = ltcdHomeDir
}
confFile := filepath.Join(homeDir, fmt.Sprintf("%v.conf", daemonName))
rpcUser, rpcPass, err := extractRPCParams(confFile)
if err != nil {
return fmt.Errorf("unable to extract RPC "+
"credentials: %v, cannot start w/o RPC connection",
err)
confFile = filepath.Join(homeDir, fmt.Sprintf("%v.conf", confFile))
switch cConfig.Node {
case "btcd":
nConf := nodeConfig.(*btcdConfig)
rpcUser, rpcPass, err := extractBtcdRPCParams(confFile)
if err != nil {
return fmt.Errorf("unable to extract RPC credentials:"+
" %v, cannot start w/o RPC connection",
err)
}
nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
case "bitcoind":
nConf := nodeConfig.(*bitcoindConfig)
rpcUser, rpcPass, zmqPath, err := extractBitcoindRPCParams(confFile)
if err != nil {
return fmt.Errorf("unable to extract RPC credentials:"+
" %v, cannot start w/o RPC connection",
err)
}
nConf.RPCUser, nConf.RPCPass, nConf.ZMQPath = rpcUser, rpcPass, zmqPath
}
fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
cConfig.RPCUser, cConfig.RPCPass = rpcUser, rpcPass
return nil
}
// extractRPCParams attempts to extract the RPC credentials for an existing
// extractBtcdRPCParams attempts to extract the RPC credentials for an existing
// btcd instance. The passed path is expected to be the location of btcd's
// application data directory on the target system.
func extractRPCParams(btcdConfigPath string) (string, string, error) {
func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) {
// First, we'll open up the btcd configuration file found at the target
// destination.
btcdConfigFile, err := os.Open(btcdConfigPath)
@ -592,7 +659,7 @@ func extractRPCParams(btcdConfigPath string) (string, string, error) {
defer btcdConfigFile.Close()
// With the file open extract the contents of the configuration file so
// we can attempt o locate the RPC credentials.
// we can attempt to locate the RPC credentials.
configContents, err := ioutil.ReadAll(btcdConfigFile)
if err != nil {
return "", "", err
@ -624,3 +691,100 @@ func extractRPCParams(btcdConfigPath string) (string, string, error) {
return string(userSubmatches[1]), string(passSubmatches[1]), nil
}
// extractBitcoindParams attempts to extract the RPC credentials for an
// existing bitcoind node instance. The passed path is expected to be the
// location of bitcoind's bitcoin.conf on the target system. The routine looks
// for a cookie first, optionally following the datadir configuration option in
// the bitcoin.conf. If it doesn't find one, it looks for rpcuser/rpcpassword.
func extractBitcoindRPCParams(bitcoindConfigPath string) (string, string, string, error) {
// First, we'll open up the bitcoind configuration file found at the
// target destination.
bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
if err != nil {
return "", "", "", err
}
defer bitcoindConfigFile.Close()
// With the file open extract the contents of the configuration file so
// we can attempt to locate the RPC credentials.
configContents, err := ioutil.ReadAll(bitcoindConfigFile)
if err != nil {
return "", "", "", err
}
// First, we look for the ZMQ path for raw blocks. If raw transactions
// are sent over this interface, we can also get unconfirmed txs.
zmqPathRE, err := regexp.Compile(`(?m)^\s*zmqpubrawblock=([^\s]+)`)
if err != nil {
return "", "", "", err
}
zmqPathSubmatches := zmqPathRE.FindSubmatch(configContents)
if len(zmqPathSubmatches) < 2 {
return "", "", "", fmt.Errorf("unable to find zmqpubrawblock in config")
}
// Next, we'll try to find an auth cookie. We need to detect the chain
// by seeing if one is specified in the configuration file.
dataDir := path.Dir(bitcoindConfigPath)
dataDirRE, err := regexp.Compile(`(?m)^\s*datadir=([^\s]+)`)
if err != nil {
return "", "", "", err
}
dataDirSubmatches := dataDirRE.FindSubmatch(configContents)
if dataDirSubmatches != nil {
dataDir = string(dataDirSubmatches[1])
}
chainDir := "/"
netRE, err := regexp.Compile(`(?m)^\s*(testnet|regtest)=[\d]+`)
if err != nil {
return "", "", "", err
}
netSubmatches := netRE.FindSubmatch(configContents)
if netSubmatches != nil {
switch string(netSubmatches[1]) {
case "testnet":
chainDir = "/testnet3/"
case "regtest":
chainDir = "/regtest/"
}
}
cookie, err := ioutil.ReadFile(dataDir + chainDir + ".cookie")
if err == nil {
splitCookie := strings.Split(string(cookie), ":")
if len(splitCookie) == 2 {
return splitCookie[0], splitCookie[1],
string(zmqPathSubmatches[1]), nil
}
}
// We didn't find a cookie, so we attempt to locate the RPC user using
// a regular expression. If we don't have a match for our regular
// expression then we'll exit with an error.
rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser=([^\s]+)`)
if err != nil {
return "", "", "", err
}
userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
if userSubmatches == nil {
return "", "", "", fmt.Errorf("unable to find rpcuser in config")
}
// Similarly, we'll use another regular expression to find the set
// rpcpass (if any). If we can't find the pass, then we'll exit with an
// error.
rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpassword=([^\s]+)`)
if err != nil {
return "", "", "", err
}
passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
if passSubmatches == nil {
return "", "", "", fmt.Errorf("unable to find rpcpassword in config")
}
return string(userSubmatches[1]), string(passSubmatches[1]),
string(zmqPathSubmatches[1]), nil
}

14
glide.lock generated
View File

@ -1,5 +1,5 @@
hash: d145c16f2f9cfdf4937eb8b7cdd65919a8c351593a179acc23f2cbca5b42f34b
updated: 2017-12-22T23:45:25.148488338-07:00
hash: 59d73259b4445fdb5982f2ec2538f53805e6e789c26b98b0fdfef43b7d0676a4
updated: 2018-01-12T11:04:32.444091731-07:00
imports:
- name: github.com/aead/chacha20
version: d31a916ded42d1640b9d89a26f8abd53cc96790c
@ -21,8 +21,8 @@ imports:
version: 84c8d2346e9fc8c7b947e243b9c24e6df9fd206a
- name: github.com/btcsuite/fastsha256
version: 637e656429416087660c84436a2a035d69d54e2e
- name: github.com/btcsuite/go-flags
version: 6c288d648c1cc1befcb90cb5511dcacf64ae8e61
- name: github.com/jessevdk/go-flags
version: f88afde2fa19a30cf50ba4b05b3d13bc6bae3079
- name: github.com/btcsuite/go-socks
version: 4720035b7bfd2a9bb130b1c184f8bbe41b6f0d0f
subpackages:
@ -86,8 +86,8 @@ imports:
version: 946bd9fbed05568b0f3cd188353d8aa28f38b688
subpackages:
- internal/socket
- name: github.com/pebbe/zmq4
version: 90d69e412a09549f2e90bac70fbb449081f1e5c1
- name: github.com/lightninglabs/gozmq
version: b0bbb30a99d00b25c3304994d20aac68607b75c0
- name: github.com/roasbeef/btcd
version: 9978b939c33973be19b932fa7b936079bb7ba38d
subpackages:
@ -118,7 +118,7 @@ imports:
- hdkeychain
- txsort
- name: github.com/roasbeef/btcwallet
version: 3037a033935cca719ba3c472870e7d080443a82f
version: 6c1491e092f39d6ed4e9103b9dc8322d04ca98bb
subpackages:
- chain
- internal/helpers

View File

@ -4,7 +4,7 @@ import:
version: ^1.2.1
- package: github.com/btcsuite/btclog
version: 84c8d2346e9fc8c7b947e243b9c24e6df9fd206a
- package: github.com/btcsuite/go-flags
- package: github.com/jessevdk/go-flags
- package: github.com/jrick/logrotate
version: a93b200c26cbae3bb09dd0dc2c7c7fe1468a034a
- package: github.com/davecgh/go-spew
@ -24,7 +24,7 @@ import:
- txscript
- wire
- connmgr
- package: github.com/pebbe/zmq4
- package: github.com/lightninglabs/gozmq
- package: github.com/roasbeef/btcrpcclient
version: d0f4db8b4dad0ca3d569b804f21247c3dd96acbb
- package: github.com/roasbeef/btcutil
@ -35,7 +35,7 @@ import:
- hdkeychain
- txsort
- package: github.com/roasbeef/btcwallet
version: 3037a033935cca719ba3c472870e7d080443a82f
version: 6c1491e092f39d6ed4e9103b9dc8322d04ca98bb
subpackages:
- chain
- waddrmgr

2
lnd.go
View File

@ -31,8 +31,8 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
flags "github.com/btcsuite/go-flags"
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
flags "github.com/jessevdk/go-flags"
"github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc"

View File

@ -124,10 +124,10 @@ func (cfg nodeConfig) genArgs() []string {
args = append(args, "--debuglevel=debug")
args = append(args, "--bitcoin.defaultchanconfs=1")
args = append(args, "--bitcoin.defaultremotedelay=4")
args = append(args, fmt.Sprintf("--bitcoin.rpchost=%v", cfg.RPCConfig.Host))
args = append(args, fmt.Sprintf("--bitcoin.rpcuser=%v", cfg.RPCConfig.User))
args = append(args, fmt.Sprintf("--bitcoin.rpcpass=%v", cfg.RPCConfig.Pass))
args = append(args, fmt.Sprintf("--bitcoin.rawrpccert=%v", encodedCert))
args = append(args, fmt.Sprintf("--btcd.rpchost=%v", cfg.RPCConfig.Host))
args = append(args, fmt.Sprintf("--btcd.rpcuser=%v", cfg.RPCConfig.User))
args = append(args, fmt.Sprintf("--btcd.rpcpass=%v", cfg.RPCConfig.Pass))
args = append(args, fmt.Sprintf("--btcd.rawrpccert=%v", encodedCert))
args = append(args, fmt.Sprintf("--rpcport=%v", cfg.RPCPort))
args = append(args, fmt.Sprintf("--peerport=%v", cfg.P2PPort))
args = append(args, fmt.Sprintf("--logdir=%v", cfg.LogDir))

View File

@ -183,6 +183,11 @@ type mockWalletController struct {
publishedTransactions chan *wire.MsgTx
}
// BackEnd returns "mock" to signify a mock wallet controller.
func (*mockWalletController) BackEnd() string {
return "mock"
}
// FetchInputInfo will be called to get info about the inputs to the funding
// transaction.
func (*mockWalletController) FetchInputInfo(