From 4d1a1d2799bc2b8c5e41d043b0176d0afaecf5b8 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 3 Aug 2016 22:13:10 -0700 Subject: [PATCH 1/4] chainntnfs: add cross interface implementation tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit refactors the existing chainntnfns package in order to allow more easily allow integration into the main system, by allowing one to gain access to a set of end-to-end tests for a particular ChainNotifier implementation. In order to achieve this, the existing set of tests for the only concrete implementation (`BtcdNoitifer`) have been refactored to test against all “registered” notifier interfaces registered. This is achieved by creating the concept of a “driver” for each concrete `ChainNotifer` implementation. Once a the package of a particular driver is imported, solely for the side effects, the init() method automatically registers the driver. Additionally, the documentation in various areas of the package have been cleaned up a bit. --- chainntfs/chainntfs_test.go | 1 - {chainntfs => chainntnfs}/btcdnotify/btcd.go | 19 +++-- .../btcdnotify/confheap.go | 8 +- chainntnfs/btcdnotify/driver.go | 40 ++++++++++ .../chainntfs.go => chainntnfs/interface.go | 78 ++++++++++++++++++- .../interface_test.go | 66 ++++++++++------ {chainntfs => chainntnfs}/log.go | 0 7 files changed, 180 insertions(+), 32 deletions(-) delete mode 100644 chainntfs/chainntfs_test.go rename {chainntfs => chainntnfs}/btcdnotify/btcd.go (96%) rename {chainntfs => chainntnfs}/btcdnotify/confheap.go (77%) create mode 100644 chainntnfs/btcdnotify/driver.go rename chainntfs/chainntfs.go => chainntnfs/interface.go (67%) rename chainntfs/btcdnotify/btcdnotify_test.go => chainntnfs/interface_test.go (84%) rename {chainntfs => chainntnfs}/log.go (100%) diff --git a/chainntfs/chainntfs_test.go b/chainntfs/chainntfs_test.go deleted file mode 100644 index 54a8429f..00000000 --- a/chainntfs/chainntfs_test.go +++ /dev/null @@ -1 +0,0 @@ -package chainntnfs diff --git a/chainntfs/btcdnotify/btcd.go b/chainntnfs/btcdnotify/btcd.go similarity index 96% rename from chainntfs/btcdnotify/btcd.go rename to chainntnfs/btcdnotify/btcd.go index 9a5fd015..ae83d220 100644 --- a/chainntfs/btcdnotify/btcd.go +++ b/chainntnfs/btcdnotify/btcd.go @@ -7,13 +7,20 @@ import ( "sync/atomic" "time" - "github.com/lightningnetwork/lnd/chainntfs" + "github.com/lightningnetwork/lnd/chainntnfs" "github.com/roasbeef/btcd/btcjson" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcrpcclient" "github.com/roasbeef/btcutil" ) +const ( + + // notifierType uniquely identifies this concrete implementation of the + // ChainNotifier interface. + notifierType = "btcd" +) + // BtcdNotifier implements the ChainNotifier interface using btcd's websockets // notifications. Multiple concurrent clients are supported. All notifications // are achieved via non-blocking sends on client channels. @@ -42,10 +49,10 @@ type BtcdNotifier struct { // Ensure BtcdNotifier implements the ChainNotifier interface at compile time. var _ chainntnfs.ChainNotifier = (*BtcdNotifier)(nil) -// NewBtcdNotifier returns a new BtcdNotifier instance. This function assumes -// the btcd node detailed in the passed configuration is already running, and +// New returns a new BtcdNotifier instance. This function assumes the btcd node +// detailed in the passed configuration is already running, and // willing to accept new websockets clients. -func NewBtcdNotifier(config *btcrpcclient.ConnConfig) (*BtcdNotifier, error) { +func New(config *btcrpcclient.ConnConfig) (*BtcdNotifier, error) { notifier := &BtcdNotifier{ notificationRegistry: make(chan interface{}), @@ -66,8 +73,8 @@ func NewBtcdNotifier(config *btcrpcclient.ConnConfig) (*BtcdNotifier, error) { OnRedeemingTx: notifier.onRedeemingTx, } - // Disable connecting to btcd within the btcrpcclient.New method. We defer - // establishing the connection to our .Start() method. + // Disable connecting to btcd within the btcrpcclient.New method. We + // defer establishing the connection to our .Start() method. config.DisableConnectOnNew = true config.DisableAutoReconnect = false chainConn, err := btcrpcclient.New(config, ntfnCallbacks) diff --git a/chainntfs/btcdnotify/confheap.go b/chainntnfs/btcdnotify/confheap.go similarity index 77% rename from chainntfs/btcdnotify/confheap.go rename to chainntnfs/btcdnotify/confheap.go index 0ac454cf..35345ce4 100644 --- a/chainntfs/btcdnotify/confheap.go +++ b/chainntnfs/btcdnotify/confheap.go @@ -1,17 +1,21 @@ package btcdnotify -// confEntry... +// confEntry represents an entry in the min-confirmation heap. . type confEntry struct { *confirmationsNotification triggerHeight uint32 } -// confirmationHeap... +// confirmationHeap is a list of confEntries sorted according to nearest +// "confirmation" height.Each entry within the min-confirmation heap is sorted +// according to the smallest dleta from the current blockheight to the +// triggerHeight of the next entry confirmationHeap type confirmationHeap struct { items []*confEntry } +// newConfirmationHeap returns a new confirmationHeap with zero items. func newConfirmationHeap() *confirmationHeap { var confItems []*confEntry return &confirmationHeap{confItems} diff --git a/chainntnfs/btcdnotify/driver.go b/chainntnfs/btcdnotify/driver.go new file mode 100644 index 00000000..1d3777fa --- /dev/null +++ b/chainntnfs/btcdnotify/driver.go @@ -0,0 +1,40 @@ +package btcdnotify + +import ( + "fmt" + + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/roasbeef/btcrpcclient" +) + +// createNewNotifier creates a new instance of the ChainNotifier interface +// implemented by BtcdNotifier. +func createNewNotifier(args ...interface{}) (chainntnfs.ChainNotifier, error) { + if len(args) != 1 { + return nil, fmt.Errorf("incorrect number of arguments to .New(...), "+ + "expected 1, instead passed %v", len(args)) + } + + config, ok := args[0].(*btcrpcclient.ConnConfig) + if !ok { + return nil, fmt.Errorf("first argument to btcdnotifier.New is " + + "incorrect, expected a *btcrpcclient.ConnConfig") + } + + return New(config) +} + +// init registers a driver for the BtcdNotifier concrete implementation of the +// chainntnfs.ChainNotifier interface. +func init() { + // Register the driver. + notifier := &chainntnfs.NotifierDriver{ + NotifierType: notifierType, + New: createNewNotifier, + } + + if err := chainntnfs.RegisterNotifier(notifier); err != nil { + panic(fmt.Sprintf("failed to register notifier driver '%s': %v", + notifierType, err)) + } +} diff --git a/chainntfs/chainntfs.go b/chainntnfs/interface.go similarity index 67% rename from chainntfs/chainntfs.go rename to chainntnfs/interface.go index 50571478..58d54d6a 100644 --- a/chainntfs/chainntfs.go +++ b/chainntnfs/interface.go @@ -1,6 +1,11 @@ package chainntnfs -import "github.com/roasbeef/btcd/wire" +import ( + "fmt" + "sync" + + "github.com/roasbeef/btcd/wire" +) // ChainNotifier represents a trusted source to receive notifications concerning // targeted events on the Bitcoin blockchain. The interface specification is @@ -104,3 +109,74 @@ type BlockEpoch struct { type BlockEpochEvent struct { Epochs chan *BlockEpoch // MUST be buffered. } + +// NotifierDriver represents a "driver" for a particular interface. A driver is +// indentified by a globally unique string identifier along with a 'New()' +// method which is responsible for initializing a particular ChainNotifier +// concrete implementation. +type NotifierDriver struct { + // NotifierType is a string which uniquely identifes the ChainNotifier + // that this driver, drives. + NotifierType string + + // New creates a new instance of a concrete ChainNotifier + // implementation given a variadic set up arguments. The function takes + // a varidaic number of interface paramters in order to provide + // initialization flexibility, thereby accomodating several potential + // ChainNotifier implementations. + New func(args ...interface{}) (ChainNotifier, error) +} + +var ( + notifiers = make(map[string]*NotifierDriver) + registerMtx sync.Mutex +) + +// RegisteredNotifiers returns a slice of all currently registered notifiers. +// +// NOTE: This function is safe for concurrent access. +func RegisteredNotifiers() []*NotifierDriver { + registerMtx.Lock() + defer registerMtx.Unlock() + + drivers := make([]*NotifierDriver, 0, len(notifiers)) + for _, driver := range notifiers { + drivers = append(drivers, driver) + } + + return drivers +} + +// RegisterNotifier registers a NotifierDriver which is capable of driving a +// concrete ChainNotifier interface. In the case that this driver has already +// been registered, an error is returned. +// +// NOTE: This function is safe for concurrent access. +func RegisterNotifier(driver *NotifierDriver) error { + registerMtx.Lock() + defer registerMtx.Unlock() + + if _, ok := notifiers[driver.NotifierType]; ok { + return fmt.Errorf("notifier already registered") + } + + notifiers[driver.NotifierType] = driver + + return nil +} + +// SupportedNotifiers returns a slice of strings that represent the database +// drivers that have been registered and are therefore supported. +// +// NOTE: This function is safe for concurrent access. +func SupportedNotifiers() []string { + registerMtx.Lock() + defer registerMtx.Unlock() + + supportedNotifiers := make([]string, 0, len(notifiers)) + for driverName := range notifiers { + supportedNotifiers = append(supportedNotifiers, driverName) + } + + return supportedNotifiers +} diff --git a/chainntfs/btcdnotify/btcdnotify_test.go b/chainntnfs/interface_test.go similarity index 84% rename from chainntfs/btcdnotify/btcdnotify_test.go rename to chainntnfs/interface_test.go index e2c21bbc..e5febffc 100644 --- a/chainntfs/btcdnotify/btcdnotify_test.go +++ b/chainntnfs/interface_test.go @@ -1,11 +1,13 @@ -package btcdnotify +package chainntnfs_test import ( "bytes" "testing" "time" - "github.com/lightningnetwork/lnd/chainntfs" + "github.com/lightningnetwork/lnd/chainntnfs" + _ "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify" + "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/rpctest" @@ -181,7 +183,7 @@ func testSpendNotification(miner *rpctest.Harness, notifier chainntnfs.ChainNotifier, t *testing.T) { // We'd like to test the spend notifiations for all - // chainntnfs.ChainNotifier concrete implemenations. + // ChainNotifier concrete implemenations. // // To do so, we first create a new output to our test target // address. @@ -286,15 +288,22 @@ var ntfnTests = []func(node *rpctest.Harness, notifier chainntnfs.ChainNotifier, testSpendNotification, } -// TODO(roasbeef): make test generic across all interfaces? -// * indeed! -// * requires interface implementation registration -func TestBtcdNotifier(t *testing.T) { - +// TestInterfaces tests all registered interfaces with a unified set of tests +// which excersie each of the required methods found within the ChainNotifier +// interface. +// +// NOTE: In the future, when additional implementations of the ChainNotifier +// interface have been implemented, in order to ensure the new concrete +// implementation is automatically tested, two steps must be undertaken. First, +// one needs add a "non-captured" (_) import from the new sub-package. This +// import should trigger an init() method within the package which registeres +// the interface. Second, an additional case in the switch within the main loop +// below needs to be added which properly initializes the interface. +func TestInterfaces(t *testing.T) { // Initialize the harness around a btcd node which will serve as our - // dedicated miner to generate blocks, cause re-orgs, etc. We'll set - // up this node with a chain length of 125, so we have plentyyy of BTC - // to play around with. + // dedicated miner to generate blocks, cause re-orgs, etc. We'll set up + // this node with a chain length of 125, so we have plentyyy of BTC to + // play around with. miner, err := rpctest.New(netParams, nil, nil) if err != nil { t.Fatalf("unable to create mining node: %v", err) @@ -304,17 +313,30 @@ func TestBtcdNotifier(t *testing.T) { t.Fatalf("unable to set up mining node: %v", err) } - nodeConfig := miner.RPCConfig() - notifier, err := NewBtcdNotifier(&nodeConfig) - if err != nil { - t.Fatalf("unable to create notifier: %v", err) - } - if err := notifier.Start(); err != nil { - t.Fatalf("unable to start notifier: %v", err) - } - defer notifier.Stop() + rpcConfig := miner.RPCConfig() - for _, ntfnTest := range ntfnTests { - ntfnTest(miner, notifier, t) + var notifier chainntnfs.ChainNotifier + for _, notifierDriver := range chainntnfs.RegisteredNotifiers() { + notifierType := notifierDriver.NotifierType + + switch notifierType { + case "btcd": + notifier, err = notifierDriver.New(&rpcConfig) + if err != nil { + t.Fatalf("unable to create %v notifier: %v", + notifierType, err) + } + } + + if err := notifier.Start(); err != nil { + t.Fatalf("unable to start notifier %v: %v", + notifierType, err) + } + + for _, ntfnTest := range ntfnTests { + ntfnTest(miner, notifier, t) + } + + notifier.Stop() } } diff --git a/chainntfs/log.go b/chainntnfs/log.go similarity index 100% rename from chainntfs/log.go rename to chainntnfs/log.go From 366d076eda0245c6a17ed85fec89f3e413c76fc9 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 3 Aug 2016 22:25:32 -0700 Subject: [PATCH 2/4] lnd: properly initialize the htlcSwitch's quit channel --- htlcswitch.go | 1 + 1 file changed, 1 insertion(+) diff --git a/htlcswitch.go b/htlcswitch.go index f4af64a8..aca430cc 100644 --- a/htlcswitch.go +++ b/htlcswitch.go @@ -89,6 +89,7 @@ func newHtlcSwitch() *htlcSwitch { linkControl: make(chan interface{}), htlcPlex: make(chan *htlcPacket, htlcQueueSize), outgoingPayments: make(chan *htlcPacket, htlcQueueSize), + quit: make(chan struct{}), } } From 8bbd010f745d185703e140f78df484921c60e3ca Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 3 Aug 2016 22:31:20 -0700 Subject: [PATCH 3/4] lnwallet: use the ChainNotifier interface throughout instead of BtcdNotifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit refactors the code within lnwallet interacting with the ChainNotifier to accept, and call against the implementation rather than a single concrete implementation. LightningWallet no longer creates it’s own BtcdNotifier implementation doing construction, now instead accepting a pre-started `ChainNotifier` interface. All imports have been updated to reflect the new naming scheme. --- lnwallet/channel.go | 2 +- lnwallet/interface.go | 6 ++--- lnwallet/wallet.go | 55 +++++++++++------------------------------ lnwallet/wallet_test.go | 11 ++++++++- 4 files changed, 28 insertions(+), 46 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 6b796172..982c5029 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -8,7 +8,7 @@ import ( "github.com/btcsuite/fastsha256" "github.com/davecgh/go-spew/spew" - "github.com/lightningnetwork/lnd/chainntfs" + "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwire" diff --git a/lnwallet/interface.go b/lnwallet/interface.go index b1ab4a94..087556a7 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -13,9 +13,9 @@ import ( // such as: uspv, btcwallet, Bitcoin Core, Electrum, etc. This interface then // serves as a "base wallet", with Lightning Network awareness taking place at // a "higher" level of abstraction. Essentially, an overlay wallet. -// Implementors of this interface must closely adhere to the documented behavior -// of all interface methods in order to ensure identical behavior accross all -// concrete implementations. +// Implementors of this interface must closely adhere to the documented +// behavior of all interface methods in order to ensure identical behavior +// across all concrete implementations. type WalletController interface { // ConfirmedBalance returns the sum of all the wallet's unspent outputs // that have at least confs confirmations. If confs is set to zero, diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 356d193b..c5b69a35 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -9,8 +9,7 @@ import ( "sync/atomic" "github.com/davecgh/go-spew/spew" - "github.com/lightningnetwork/lnd/chainntfs" - "github.com/lightningnetwork/lnd/chainntfs/btcdnotify" + "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/elkrem" "github.com/roasbeef/btcd/btcjson" @@ -18,7 +17,6 @@ import ( "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" - "github.com/roasbeef/btcrpcclient" "github.com/roasbeef/btcutil" "github.com/roasbeef/btcutil/coinset" "github.com/roasbeef/btcutil/txsort" @@ -234,8 +232,7 @@ type LightningWallet struct { // Used by in order to obtain notifications about funding transaction // reaching a specified confirmation depth, and to catch // counterparty's broadcasting revoked commitment states. - // TODO(roasbeef): needs to be stripped out from wallet - ChainNotifier chainntnfs.ChainNotifier + chainNotifier chainntnfs.ChainNotifier // The core wallet, all non Lightning Network specific interaction is // proxied to the internal wallet. @@ -279,7 +276,12 @@ type LightningWallet struct { // NewLightningWallet creates/opens and initializes a LightningWallet instance. // If the wallet has never been created (according to the passed dataDir), first-time // setup is executed. -func NewLightningWallet(config *Config, cdb *channeldb.DB) (*LightningWallet, error) { +// +// NOTE: The passed channeldb, and ChainNotifier should already be fully +// initialized/started before being passed as a function arugment. +func NewLightningWallet(config *Config, cdb *channeldb.DB, + notifier chainntnfs.ChainNotifier) (*LightningWallet, error) { + // Ensure the wallet exists or create it when the create flag is set. netDir := networkDir(config.DataDir, config.NetParams) @@ -344,26 +346,8 @@ func NewLightningWallet(config *Config, cdb *channeldb.DB) (*LightningWallet, er return nil, err } - // Using the same authentication info, create a config for a second - // rpcclient which will be used by the current default chain - // notifier implemenation. - rpcConfig := &btcrpcclient.ConnConfig{ - Host: config.RpcHost, - Endpoint: "ws", - User: config.RpcUser, - Pass: config.RpcPass, - Certificates: config.CACert, - DisableTLS: false, - DisableConnectOnNew: true, - DisableAutoReconnect: false, - } - chainNotifier, err := btcdnotify.NewBtcdNotifier(rpcConfig) - if err != nil { - return nil, err - } - return &LightningWallet{ - ChainNotifier: chainNotifier, + chainNotifier: notifier, rpc: rpcc, Wallet: wallet, channelDB: cdb, @@ -393,17 +377,8 @@ func (l *LightningWallet) Startup() error { } l.Start() - // Start the notification server. This is used so channel managment - // goroutines can be notified when a funding transaction reaches a - // sufficient number of confirmations, or when the input for the funding - // transaction is spent in an attempt at an uncooperative close by the - // counter party. - if err := l.ChainNotifier.Start(); err != nil { - return err - } - - // Pass the rpc client into the wallet so it can sync up to the current - // main chain. + // Pass the rpc client into the wallet so it can sync up to the + // current main chain. l.SynchronizeRPC(l.rpc) l.wg.Add(1) @@ -426,8 +401,6 @@ func (l *LightningWallet) Shutdown() error { l.rpc.Shutdown() - l.ChainNotifier.Stop() - close(l.quit) l.wg.Wait() return nil @@ -1251,7 +1224,7 @@ func (l *LightningWallet) handleChannelOpen(req *channelOpenMsg) { // Finally, create and officially open the payment channel! // TODO(roasbeef): CreationTime once tx is 'open' - channel, _ := NewLightningChannel(l, l.ChainNotifier, l.channelDB, + channel, _ := NewLightningChannel(l, l.chainNotifier, l.channelDB, res.partialState) res.chanOpen <- channel @@ -1266,7 +1239,7 @@ func (l *LightningWallet) openChannelAfterConfirmations(res *ChannelReservation) // transaction reaches `numConfs` confirmations. txid := res.fundingTx.TxSha() numConfs := uint32(res.numConfsToOpen) - confNtfn, _ := l.ChainNotifier.RegisterConfirmationsNtfn(&txid, numConfs) + confNtfn, _ := l.chainNotifier.RegisterConfirmationsNtfn(&txid, numConfs) walletLog.Infof("Waiting for funding tx (txid: %v) to reach %v confirmations", txid, numConfs) @@ -1293,7 +1266,7 @@ out: // Finally, create and officially open the payment channel! // TODO(roasbeef): CreationTime once tx is 'open' - channel, _ := NewLightningChannel(l, l.ChainNotifier, l.channelDB, + channel, _ := NewLightningChannel(l, l.chainNotifier, l.channelDB, res.partialState) res.chanOpen <- channel } diff --git a/lnwallet/wallet_test.go b/lnwallet/wallet_test.go index 7cb8846c..330401c9 100644 --- a/lnwallet/wallet_test.go +++ b/lnwallet/wallet_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/boltdb/bolt" + "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify" "github.com/lightningnetwork/lnd/channeldb" "github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcutil/txsort" @@ -338,7 +339,15 @@ func createTestWallet(miningNode *rpctest.Harness, netParams *chaincfg.Params) ( return "", nil, err } - wallet, err := NewLightningWallet(config, cdb) + chainNotifier, err := btcdnotify.New(&rpcConfig) + if err != nil { + return "", nil, err + } + if err := chainNotifier.Start(); err != nil { + return "", nil, err + } + + wallet, err := NewLightningWallet(config, cdb, chainNotifier) if err != nil { return "", nil, err } From 1b682b0f40ef6f56cdde4a89db6e777d00157e9d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 4 Aug 2016 12:37:50 -0700 Subject: [PATCH 4/4] lnd: update server initialization due to ChainNotifier changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit modifies the daemon’s initialization within the `lndMain` method to create an instance of the current default ChainNotifier outside of the LightningWallet. At this point, since there are no other implementations of the ChainNotifier, the current concrete implementation BtcdNotifier is used by default. In the future, once other ChainNotifier implementations are in place, config parsing should be fed into a factory function which creates the proper ChainNotifier implementation. Finally, several imports have been updated to reflect the change in package name. --- lnd.go | 37 ++++++++++++++++++++++++----- log.go | 2 +- peer.go | 5 ++-- server.go | 70 +++++++++++++++++++++++++++++++++++++------------------ 4 files changed, 81 insertions(+), 33 deletions(-) diff --git a/lnd.go b/lnd.go index c478fdba..efcb9a1d 100644 --- a/lnd.go +++ b/lnd.go @@ -14,9 +14,11 @@ import ( "google.golang.org/grpc" + "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/roasbeef/btcrpcclient" ) var ( @@ -94,9 +96,29 @@ func lndMain() error { return err } - // Create, and start the lnwallet, which handles the core payment channel - // logic, and exposes control via proxy state machines. - config := &lnwallet.Config{ + btcdHost := fmt.Sprintf("%v:%v", loadedConfig.RPCHost, activeNetParams.rpcPort) + btcdUser := loadedConfig.RPCUser + btcdPass := loadedConfig.RPCPass + + // TODO(roasbeef): parse config here and select chosen notifier instead + rpcConfig := &btcrpcclient.ConnConfig{ + Host: btcdHost, + Endpoint: "ws", + User: btcdUser, + Pass: btcdPass, + Certificates: rpcCert, + DisableTLS: false, + DisableConnectOnNew: true, + DisableAutoReconnect: false, + } + notifier, err := btcdnotify.New(rpcConfig) + if err != nil { + return err + } + + // Create, and start the lnwallet, which handles the core payment + // channel logic, and exposes control via proxy state machines. + walletConfig := &lnwallet.Config{ PrivatePass: []byte("hello"), DataDir: filepath.Join(loadedConfig.DataDir, "lnwallet"), RpcHost: fmt.Sprintf("%v:%v", rpcIP[0], activeNetParams.rpcPort), @@ -105,7 +127,7 @@ func lndMain() error { CACert: rpcCert, NetParams: activeNetParams.Params, } - wallet, err := lnwallet.NewLightningWallet(config, chanDB) + wallet, err := lnwallet.NewLightningWallet(walletConfig, chanDB, notifier) if err != nil { fmt.Printf("unable to create wallet: %v\n", err) return err @@ -124,12 +146,15 @@ func lndMain() error { defaultListenAddrs := []string{ net.JoinHostPort("", strconv.Itoa(loadedConfig.PeerPort)), } - server, err := newServer(defaultListenAddrs, wallet, chanDB) + server, err := newServer(defaultListenAddrs, notifier, wallet, chanDB) if err != nil { srvrLog.Errorf("unable to create server: %v\n", err) return err } - server.Start() + if err := server.Start(); err != nil { + srvrLog.Errorf("unable to create to start: %v\n", err) + return err + } addInterruptHandler(func() { ltndLog.Infof("Gracefully shutting down the server...") diff --git a/log.go b/log.go index cdb52210..07a56b50 100644 --- a/log.go +++ b/log.go @@ -6,7 +6,7 @@ import ( "github.com/btcsuite/btclog" "github.com/btcsuite/seelog" - "github.com/lightningnetwork/lnd/chainntfs" + "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwallet" ) diff --git a/peer.go b/peer.go index f70fee94..a5f73ceb 100644 --- a/peer.go +++ b/peer.go @@ -218,7 +218,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error { for _, dbChan := range chans { chanID := dbChan.ChanID lnChan, err := lnwallet.NewLightningChannel(p.server.lnwallet, - p.server.lnwallet.ChainNotifier, p.server.chanDB, dbChan) + p.server.chainNotifier, p.server.chanDB, dbChan) if err != nil { return err } @@ -673,8 +673,7 @@ func (p *peer) handleLocalClose(req *closeLinkReq) { // confirmation. go func() { // TODO(roasbeef): add param for num needed confs - notifier := p.server.lnwallet.ChainNotifier - confNtfn, err := notifier.RegisterConfirmationsNtfn(txid, 1) + confNtfn, err := p.server.chainNotifier.RegisterConfirmationsNtfn(txid, 1) if err != nil { req.err <- err return diff --git a/server.go b/server.go index 29b9869d..e8c591e2 100644 --- a/server.go +++ b/server.go @@ -8,6 +8,7 @@ import ( "sync/atomic" "github.com/btcsuite/fastsha256" + "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lndc" "github.com/lightningnetwork/lnd/lnrpc" @@ -40,9 +41,9 @@ type server struct { listeners []net.Listener peers map[int32]*peer - rpcServer *rpcServer - // TODO(roasbeef): add chan notifier also - lnwallet *lnwallet.LightningWallet + rpcServer *rpcServer + chainNotifier chainntnfs.ChainNotifier + lnwallet *lnwallet.LightningWallet // TODO(roasbeef): add to constructor fundingMgr *fundingManager @@ -63,8 +64,8 @@ type server struct { // newServer creates a new instance of the server which is to listen using the // passed listener address. -func newServer(listenAddrs []string, wallet *lnwallet.LightningWallet, - chanDB *channeldb.DB) (*server, error) { +func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier, + wallet *lnwallet.LightningWallet, chanDB *channeldb.DB) (*server, error) { privKey, err := getIdentityPrivKey(chanDB, wallet) if err != nil { @@ -81,19 +82,20 @@ func newServer(listenAddrs []string, wallet *lnwallet.LightningWallet, serializedPubKey := privKey.PubKey().SerializeCompressed() s := &server{ - chanDB: chanDB, - fundingMgr: newFundingManager(wallet), - htlcSwitch: newHtlcSwitch(), - invoices: newInvoiceRegistry(), - lnwallet: wallet, - identityPriv: privKey, - lightningID: fastsha256.Sum256(serializedPubKey), - listeners: listeners, - peers: make(map[int32]*peer), - newPeers: make(chan *peer, 100), - donePeers: make(chan *peer, 100), - queries: make(chan interface{}), - quit: make(chan struct{}), + chainNotifier: notifier, + chanDB: chanDB, + fundingMgr: newFundingManager(wallet), + htlcSwitch: newHtlcSwitch(), + invoices: newInvoiceRegistry(), + lnwallet: wallet, + identityPriv: privKey, + lightningID: fastsha256.Sum256(serializedPubKey), + listeners: listeners, + peers: make(map[int32]*peer), + newPeers: make(chan *peer, 100), + donePeers: make(chan *peer, 100), + queries: make(chan interface{}), + quit: make(chan struct{}), } // TODO(roasbeef): remove @@ -110,10 +112,10 @@ func newServer(listenAddrs []string, wallet *lnwallet.LightningWallet, // Start starts the main daemon server, all requested listeners, and any helper // goroutines. -func (s *server) Start() { +func (s *server) Start() error { // Already running? if atomic.AddInt32(&s.started, 1) != 1 { - return + return nil } // Start all the listeners. @@ -122,12 +124,30 @@ func (s *server) Start() { go s.listener(l) } - s.fundingMgr.Start() - s.htlcSwitch.Start() + // Start the notification server. This is used so channel managment + // goroutines can be notified when a funding transaction reaches a + // sufficient number of confirmations, or when the input for the + // funding transaction is spent in an attempt at an uncooperative + // close by the counter party. + if err := s.chainNotifier.Start(); err != nil { + return err + } + + if err := s.rpcServer.Start(); err != nil { + return err + } + if err := s.fundingMgr.Start(); err != nil { + return err + } + if err := s.htlcSwitch.Start(); err != nil { + return err + } s.routingMgr.Start() s.wg.Add(1) go s.queryHandler() + + return nil } // Stop gracefully shutsdown the main daemon server. This function will signal @@ -146,10 +166,14 @@ func (s *server) Stop() error { } } + // Shutdown the wallet, funding manager, and the rpc server. + s.chainNotifier.Stop() s.rpcServer.Stop() - s.lnwallet.Shutdown() s.fundingMgr.Stop() s.routingMgr.Stop() + s.htlcSwitch.Stop() + + s.lnwallet.Shutdown() // Signal all the lingering goroutines to quit. close(s.quit)