diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 96e2ddc2c..ec8a0df92 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -166,6 +166,11 @@ func (m *mockChanEvent) NotifyPendingOpenChannelEvent(outpoint wire.OutPoint, } } +type newChannelMsg struct { + channel *channeldb.OpenChannel + err chan error +} + type testNode struct { privKey *btcec.PrivateKey addr *lnwire.NetAddress diff --git a/lnpeer/peer.go b/lnpeer/peer.go index 1b118bfb1..fb6589dd2 100644 --- a/lnpeer/peer.go +++ b/lnpeer/peer.go @@ -9,8 +9,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" ) -// Peer is an interface which represents the remote lightning node inside our -// system. +// Peer is an interface which represents a remote lightning node. type Peer interface { // SendMessage sends a variadic number of high-priority message to // remote peer. The first argument denotes if the method should block diff --git a/lnwire/commit_sig.go b/lnwire/commit_sig.go index 72c235b34..2455c0165 100644 --- a/lnwire/commit_sig.go +++ b/lnwire/commit_sig.go @@ -89,7 +89,7 @@ func (c *CommitSig) MaxPayloadLength(uint32) uint32 { // TargetChanID returns the channel id of the link for which this message is // intended. // -// NOTE: Part of lnd.LinkUpdater interface. +// NOTE: Part of peer.LinkUpdater interface. func (c *CommitSig) TargetChanID() ChannelID { return c.ChanID } diff --git a/lnwire/revoke_and_ack.go b/lnwire/revoke_and_ack.go index f63951083..d685f0f32 100644 --- a/lnwire/revoke_and_ack.go +++ b/lnwire/revoke_and_ack.go @@ -85,7 +85,7 @@ func (c *RevokeAndAck) MaxPayloadLength(uint32) uint32 { // TargetChanID returns the channel id of the link for which this message is // intended. // -// NOTE: Part of lnd.LinkUpdater interface. +// NOTE: Part of peer.LinkUpdater interface. func (c *RevokeAndAck) TargetChanID() ChannelID { return c.ChanID } diff --git a/lnwire/update_add_htlc.go b/lnwire/update_add_htlc.go index b3add950b..028c6320d 100644 --- a/lnwire/update_add_htlc.go +++ b/lnwire/update_add_htlc.go @@ -113,7 +113,7 @@ func (c *UpdateAddHTLC) MaxPayloadLength(uint32) uint32 { // TargetChanID returns the channel id of the link for which this message is // intended. // -// NOTE: Part of lnd.LinkUpdater interface. +// NOTE: Part of peer.LinkUpdater interface. func (c *UpdateAddHTLC) TargetChanID() ChannelID { return c.ChanID } diff --git a/lnwire/update_fail_htlc.go b/lnwire/update_fail_htlc.go index 17fc3cd4c..194f2ecd0 100644 --- a/lnwire/update_fail_htlc.go +++ b/lnwire/update_fail_htlc.go @@ -89,7 +89,7 @@ func (c *UpdateFailHTLC) MaxPayloadLength(uint32) uint32 { // TargetChanID returns the channel id of the link for which this message is // intended. // -// NOTE: Part of lnd.LinkUpdater interface. +// NOTE: Part of peer.LinkUpdater interface. func (c *UpdateFailHTLC) TargetChanID() ChannelID { return c.ChanID } diff --git a/lnwire/update_fail_malformed_htlc.go b/lnwire/update_fail_malformed_htlc.go index 68f0a61b8..39d4b8709 100644 --- a/lnwire/update_fail_malformed_htlc.go +++ b/lnwire/update_fail_malformed_htlc.go @@ -77,7 +77,7 @@ func (c *UpdateFailMalformedHTLC) MaxPayloadLength(uint32) uint32 { // TargetChanID returns the channel id of the link for which this message is // intended. // -// NOTE: Part of lnd.LinkUpdater interface. +// NOTE: Part of peer.LinkUpdater interface. func (c *UpdateFailMalformedHTLC) TargetChanID() ChannelID { return c.ChanID } diff --git a/lnwire/update_fee.go b/lnwire/update_fee.go index 5657633b0..2d27c3772 100644 --- a/lnwire/update_fee.go +++ b/lnwire/update_fee.go @@ -72,7 +72,7 @@ func (c *UpdateFee) MaxPayloadLength(uint32) uint32 { // TargetChanID returns the channel id of the link for which this message is // intended. // -// NOTE: Part of lnd.LinkUpdater interface. +// NOTE: Part of peer.LinkUpdater interface. func (c *UpdateFee) TargetChanID() ChannelID { return c.ChanID } diff --git a/lnwire/update_fulfill_htlc.go b/lnwire/update_fulfill_htlc.go index 49344008a..6c0e6339f 100644 --- a/lnwire/update_fulfill_htlc.go +++ b/lnwire/update_fulfill_htlc.go @@ -82,7 +82,7 @@ func (c *UpdateFulfillHTLC) MaxPayloadLength(uint32) uint32 { // TargetChanID returns the channel id of the link for which this message is // intended. // -// NOTE: Part of lnd.LinkUpdater interface. +// NOTE: Part of peer.LinkUpdater interface. func (c *UpdateFulfillHTLC) TargetChanID() ChannelID { return c.ChanID } diff --git a/log.go b/log.go index bf1949693..f809acdf8 100644 --- a/log.go +++ b/log.go @@ -30,6 +30,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chanfunding" "github.com/lightningnetwork/lnd/monitoring" "github.com/lightningnetwork/lnd/netann" + "github.com/lightningnetwork/lnd/peer" "github.com/lightningnetwork/lnd/peernotifier" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/localchans" @@ -75,7 +76,6 @@ var ( // function should always be called as soon as possible to finish // setting them up properly with a root logger. ltndLog = addLndPkgLogger("LTND") - peerLog = addLndPkgLogger("PEER") rpcsLog = addLndPkgLogger("RPCS") srvrLog = addLndPkgLogger("SRVR") fndgLog = addLndPkgLogger("FNDG") @@ -122,6 +122,7 @@ func SetupLoggers(root *build.RotatingLogWriter) { AddSubLogger(root, "WTCL", wtclient.UseLogger) AddSubLogger(root, "PRNF", peernotifier.UseLogger) AddSubLogger(root, "CHFD", chanfunding.UseLogger) + AddSubLogger(root, "PEER", peer.UseLogger) AddSubLogger(root, "CHCL", chancloser.UseLogger) AddSubLogger(root, routing.Subsystem, routing.UseLogger, localchans.UseLogger) diff --git a/peer.go b/peer/brontide.go similarity index 81% rename from peer.go rename to peer/brontide.go index 6667fe95f..948900f18 100644 --- a/peer.go +++ b/peer/brontide.go @@ -1,4 +1,4 @@ -package lnd +package peer import ( "bytes" @@ -29,7 +29,6 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chancloser" "github.com/lightningnetwork/lnd/lnwire" - "github.com/lightningnetwork/lnd/pool" "github.com/lightningnetwork/lnd/queue" "github.com/lightningnetwork/lnd/ticker" ) @@ -41,14 +40,16 @@ const ( // idleTimeout is the duration of inactivity before we time out a peer. idleTimeout = 5 * time.Minute - // writeMessageTimeout is the timeout used when writing a message to peer. + // writeMessageTimeout is the timeout used when writing a message to the + // peer. writeMessageTimeout = 5 * time.Second // readMessageTimeout is the timeout used when reading a message from a // peer. readMessageTimeout = 5 * time.Second - // handshakeTimeout is the timeout used when waiting for peer init message. + // handshakeTimeout is the timeout used when waiting for the peer's init + // message. handshakeTimeout = 15 * time.Second // outgoingQueueLen is the buffer size of the channel which houses @@ -56,8 +57,15 @@ const ( // this struct. outgoingQueueLen = 50 - // errorBufferSize is the number of historic peer errors that we store. - errorBufferSize = 10 + // ErrorBufferSize is the number of historic peer errors that we store. + ErrorBufferSize = 10 +) + +var ( + // ErrChannelNotFound is an error returned when a channel is queried and + // either the Brontide doesn't know of it, or the channel in question + // is pending. + ErrChannelNotFound = fmt.Errorf("channel not found") ) // outgoingMsg packages an lnwire.Message to be sent out on the wire, along with @@ -70,14 +78,14 @@ type outgoingMsg struct { } // newChannelMsg packages a channeldb.OpenChannel with a channel that allows -// the receiver of the request to report when the funding transaction has been -// confirmed and the channel creation process completed. +// the receiver of the request to report when the channel creation process has +// completed. type newChannelMsg struct { channel *channeldb.OpenChannel err chan error } -// closeMsgs is a wrapper struct around any wire messages that deal with the +// closeMsg is a wrapper struct around any wire messages that deal with the // cooperative channel closure negotiation process. This struct includes the // raw channel ID targeted along with the original message. type closeMsg struct { @@ -85,70 +93,61 @@ type closeMsg struct { msg lnwire.Message } -// pendingUpdate describes the pending state of a closing channel. -type pendingUpdate struct { +// PendingUpdate describes the pending state of a closing channel. +type PendingUpdate struct { Txid []byte OutputIndex uint32 } -// channelCloseUpdate contains the outcome of the close channel operation. -type channelCloseUpdate struct { +// ChannelCloseUpdate contains the outcome of the close channel operation. +type ChannelCloseUpdate struct { ClosingTxid []byte Success bool } -// timestampedError is a timestamped error that is used to store the most recent +// TimestampedError is a timestamped error that is used to store the most recent // errors we have experienced with our peers. -type timestampedError struct { - error error - timestamp time.Time +type TimestampedError struct { + Error error + Timestamp time.Time } -// peer is an active peer on the Lightning Network. This struct is responsible +// Brontide is an active peer on the Lightning Network. This struct is responsible // for managing any channel state related to this peer. To do so, it has // several helper goroutines to handle events such as HTLC timeouts, new // funding workflow, and detecting an uncooperative closure of any active // channels. // TODO(roasbeef): proper reconnection logic -type peer struct { +type Brontide struct { // MUST be used atomically. started int32 disconnect int32 - // The following fields are only meant to be used *atomically* + // MUST be used atomically. bytesReceived uint64 bytesSent uint64 // pingTime is a rough estimate of the RTT (round-trip-time) between us - // and the connected peer. This time is expressed in micro seconds. + // and the connected peer. This time is expressed in microseconds. // To be used atomically. // TODO(roasbeef): also use a WMA or EMA? pingTime int64 // pingLastSend is the Unix time expressed in nanoseconds when we sent - // our last ping message. To be used atomically. + // our last ping message. To be used atomically. pingLastSend int64 - cfg *Config - - connReq *connmgr.ConnReq - conn net.Conn - - addr *lnwire.NetAddress - pubKeyBytes [33]byte + cfg Config // activeSignal when closed signals that the peer is now active and // ready to process messages. activeSignal chan struct{} - // startTime is the time this peer connection was successfully - // established. It will be zero for peers that did not successfully - // Start(). + // startTime is the time this peer connection was successfully established. + // It will be zero for peers that did not successfully call Start(). startTime time.Time - inbound bool - - // sendQueue is the channel which is used to queue outgoing to be + // sendQueue is the channel which is used to queue outgoing messages to be // written onto the wire. Note that this channel is unbuffered. sendQueue chan outgoingMsg @@ -157,7 +156,7 @@ type peer struct { outgoingQueue chan outgoingMsg // activeChanMtx protects access to the activeChannels and - // addeddChannels maps. + // addedChannels maps. activeChanMtx sync.RWMutex // activeChannels is a map which stores the state machines of all @@ -186,11 +185,10 @@ type peer struct { // proxy messages to individual, active links. activeMsgStreams map[lnwire.ChannelID]*msgStream - // activeChanCloses is a map that keep track of all the active - // cooperative channel closures that are active. Any channel closing - // messages are directed to one of these active state machines. Once - // the channel has been closed, the state machine will be delete from - // the map. + // activeChanCloses is a map that keeps track of all the active + // cooperative channel closures. Any channel closing messages are directed + // to one of these active state machines. Once the channel has been closed, + // the state machine will be deleted from the map. activeChanCloses map[lnwire.ChannelID]*chancloser.ChanCloser // localCloseChanReqs is a channel in which any local requests to close @@ -206,28 +204,6 @@ type peer struct { // well as lnwire.ClosingSigned messages. chanCloseMsgs chan *closeMsg - // chanActiveTimeout specifies the duration the peer will wait to - // request a channel reenable, beginning from the time the peer was - // started. - chanActiveTimeout time.Duration - - server *server - - // features is the set of features that we advertised to the remote - // node. - features *lnwire.FeatureVector - - // legacyFeatures is the set of features that we advertised to the remote - // node for backwards compatibility. Nodes that have not implemented - // flat featurs will still be able to read our feature bits from the - // legacy global field, but we will also advertise everything in the - // default features field. - legacyFeatures *lnwire.FeatureVector - - // outgoingCltvRejectDelta defines the number of blocks before expiry of - // an htlc where we don't offer an htlc anymore. - outgoingCltvRejectDelta uint32 - // remoteFeatures is the feature vector received from the peer during // the connection handshake. remoteFeatures *lnwire.FeatureVector @@ -238,95 +214,41 @@ type peer struct { // peer's chansync message with its own over and over again. resentChanSyncMsg map[lnwire.ChannelID]struct{} - // errorBuffer stores a set of errors related to a peer. It contains - // error messages that our peer has recently sent us over the wire and - // records of unknown messages that were sent to us and, so that we can - // track a full record of the communication errors we have had with our - // peer. If we choose to disconnect from a peer, it also stores the - // reason we had for disconnecting. - errorBuffer *queue.CircularBuffer - - // writePool is the task pool to that manages reuse of write buffers. - // Write tasks are submitted to the pool in order to conserve the total - // number of write buffers allocated at any one time, and decouple write - // buffer allocation from the peer life cycle. - writePool *pool.Write - - readPool *pool.Read - queueQuit chan struct{} quit chan struct{} wg sync.WaitGroup } -// A compile-time check to ensure that peer satisfies the lnpeer.Peer interface. -var _ lnpeer.Peer = (*peer)(nil) - -// newPeer creates a new peer from an establish connection object, and a -// pointer to the main server. It takes an error buffer which may contain errors -// from a previous connection with the peer if we have been connected to them -// before. -func newPeer(cfg *Config, conn net.Conn, connReq *connmgr.ConnReq, server *server, - addr *lnwire.NetAddress, inbound bool, - features, legacyFeatures *lnwire.FeatureVector, - chanActiveTimeout time.Duration, - outgoingCltvRejectDelta uint32, - errBuffer *queue.CircularBuffer) ( - *peer, error) { - - nodePub := addr.IdentityKey - - p := &peer{ - conn: conn, - addr: addr, - - cfg: cfg, - - activeSignal: make(chan struct{}), - - inbound: inbound, - connReq: connReq, - - server: server, - - features: features, - legacyFeatures: legacyFeatures, - - outgoingCltvRejectDelta: outgoingCltvRejectDelta, - - sendQueue: make(chan outgoingMsg), - outgoingQueue: make(chan outgoingMsg), +// A compile-time check to ensure that Brontide satisfies the lnpeer.Peer interface. +var _ lnpeer.Peer = (*Brontide)(nil) +// NewBrontide creates a new Brontide from a peer.Config struct. +func NewBrontide(cfg Config) *Brontide { + p := &Brontide{ + cfg: cfg, + activeSignal: make(chan struct{}), + sendQueue: make(chan outgoingMsg), + outgoingQueue: make(chan outgoingMsg), addedChannels: make(map[lnwire.ChannelID]struct{}), activeChannels: make(map[lnwire.ChannelID]*lnwallet.LightningChannel), newChannels: make(chan *newChannelMsg, 1), - activeMsgStreams: make(map[lnwire.ChannelID]*msgStream), - + activeMsgStreams: make(map[lnwire.ChannelID]*msgStream), activeChanCloses: make(map[lnwire.ChannelID]*chancloser.ChanCloser), localCloseChanReqs: make(chan *htlcswitch.ChanClose), linkFailures: make(chan linkFailureReport), chanCloseMsgs: make(chan *closeMsg), resentChanSyncMsg: make(map[lnwire.ChannelID]struct{}), - - chanActiveTimeout: chanActiveTimeout, - - errorBuffer: errBuffer, - - writePool: server.writePool, - readPool: server.readPool, - - queueQuit: make(chan struct{}), - quit: make(chan struct{}), + queueQuit: make(chan struct{}), + quit: make(chan struct{}), } - copy(p.pubKeyBytes[:], nodePub.SerializeCompressed()) - return p, nil + return p } // Start starts all helper goroutines the peer needs for normal operations. In // the case this peer has already been started, then this function is a loop. -func (p *peer) Start() error { +func (p *Brontide) Start() error { if atomic.AddInt32(&p.started, 1) != 1 { return nil } @@ -385,7 +307,7 @@ func (p *peer) Start() error { // Fetch and then load all the active channels we have with this remote // peer from the database. - activeChans, err := p.server.chanDB.FetchOpenChannels(p.addr.IdentityKey) + activeChans, err := p.cfg.ChannelDB.FetchOpenChannels(p.cfg.Addr.IdentityKey) if err != nil { peerLog.Errorf("unable to fetch active chans "+ "for peer %v: %v", p, err) @@ -393,7 +315,7 @@ func (p *peer) Start() error { } if len(activeChans) == 0 { - p.server.prunePersistentPeerConnection(p.pubKeyBytes) + p.cfg.PrunePersistentPeerConnection(p.cfg.PubKeyBytes) } // Next, load all the active channels we have with this peer, @@ -448,17 +370,16 @@ func (p *peer) Start() error { // initGossipSync initializes either a gossip syncer or an initial routing // dump, depending on the negotiated synchronization method. -func (p *peer) initGossipSync() { - switch { +func (p *Brontide) initGossipSync() { // If the remote peer knows of the new gossip queries feature, then // we'll create a new gossipSyncer in the AuthenticatedGossiper for it. - case p.remoteFeatures.HasFeature(lnwire.GossipQueriesOptional): - srvrLog.Infof("Negotiated chan series queries with %x", - p.pubKeyBytes[:]) + if p.remoteFeatures.HasFeature(lnwire.GossipQueriesOptional) { + peerLog.Infof("Negotiated chan series queries with %x", + p.cfg.PubKeyBytes[:]) - // Register the this peer's for gossip syncer with the gossiper. - // This is blocks synchronously to ensure the gossip syncer is + // Register the peer's gossip syncer with the gossiper. + // This blocks synchronously to ensure the gossip syncer is // registered with the gossiper before attempting to read // messages from the remote peer. // @@ -466,7 +387,7 @@ func (p *peer) initGossipSync() { // requires an improved version of the current network // bootstrapper to ensure we can find and connect to non-channel // peers. - p.server.authGossiper.InitSyncState(p) + p.cfg.AuthGossiper.InitSyncState(p) } } @@ -476,7 +397,7 @@ func (p *peer) initGossipSync() { // exits. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) QuitSignal() <-chan struct{} { +func (p *Brontide) QuitSignal() <-chan struct{} { return p.quit } @@ -484,7 +405,7 @@ func (p *peer) QuitSignal() <-chan struct{} { // channels returned by the database. It returns a slice of channel reestablish // messages that should be sent to the peer immediately, in case we have borked // channels that haven't been closed yet. -func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( +func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( []lnwire.Message, error) { // Return a slice of messages to send to the peers in case the channel @@ -493,7 +414,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( for _, dbChan := range chans { lnChan, err := lnwallet.NewLightningChannel( - p.server.cc.signer, dbChan, p.server.sigPool, + p.cfg.Signer, dbChan, p.cfg.SigPool, ) if err != nil { return nil, err @@ -508,9 +429,8 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( // Skip adding any permanently irreconcilable channels to the // htlcswitch. - switch { - case !dbChan.HasChanStatus(channeldb.ChanStatusDefault) && - !dbChan.HasChanStatus(channeldb.ChanStatusRestored): + if !dbChan.HasChanStatus(channeldb.ChanStatusDefault) && + !dbChan.HasChanStatus(channeldb.ChanStatusRestored) { peerLog.Warnf("ChannelPoint(%v) has status %v, won't "+ "start.", chanPoint, dbChan.ChanStatus()) @@ -533,15 +453,10 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( continue } - _, currentHeight, err := p.server.cc.chainIO.GetBestBlock() - if err != nil { - return nil, err - } - // Before we register this new link with the HTLC Switch, we'll // need to fetch its current link-layer forwarding policy from // the database. - graph := p.server.chanDB.ChannelGraph() + graph := p.cfg.ChannelDB.ChannelGraph() info, p1, p2, err := graph.FetchChannelEdgesByOutpoint(chanPoint) if err != nil && err != channeldb.ErrEdgeNotFound { return nil, err @@ -556,7 +471,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( // particular channel. var selfPolicy *channeldb.ChannelEdgePolicy if info != nil && bytes.Equal(info.NodeKey1Bytes[:], - p.server.identityECDH.PubKey().SerializeCompressed()) { + p.cfg.ServerPubKey[:]) { selfPolicy = p1 } else { @@ -579,7 +494,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( peerLog.Warnf("Unable to find our forwarding policy "+ "for channel %v, using default values", chanPoint) - forwardingPolicy = &p.server.cc.routingPolicy + forwardingPolicy = &p.cfg.RoutingPolicy } peerLog.Tracef("Using link policy of: %v", @@ -600,7 +515,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( } // Subscribe to the set of on-chain events for this channel. - chainEvents, err := p.server.chainArb.SubscribeChannelEvents( + chainEvents, err := p.cfg.ChainArb.SubscribeChannelEvents( *chanPoint, ) if err != nil { @@ -609,7 +524,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( err = p.addLink( chanPoint, lnChan, forwardingPolicy, chainEvents, - currentHeight, true, + true, ) if err != nil { return nil, fmt.Errorf("unable to add link %v to "+ @@ -624,12 +539,12 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( return msgs, nil } -// addLink creates and adds a new link from the specified channel. -func (p *peer) addLink(chanPoint *wire.OutPoint, +// addLink creates and adds a new ChannelLink from the specified channel. +func (p *Brontide) addLink(chanPoint *wire.OutPoint, lnChan *lnwallet.LightningChannel, forwardingPolicy *htlcswitch.ForwardingPolicy, chainEvents *contractcourt.ChainEventSubscription, - currentHeight int32, syncStates bool) error { + syncStates bool) error { // onChannelFailure will be called by the link in case the channel // fails for some reason. @@ -647,29 +562,29 @@ func (p *peer) addLink(chanPoint *wire.OutPoint, select { case p.linkFailures <- failure: case <-p.quit: - case <-p.server.quit: + case <-p.cfg.Quit: } } + updateContractSignals := func(signals *contractcourt.ContractSignals) error { + return p.cfg.ChainArb.UpdateContractSignals(*chanPoint, signals) + } + linkCfg := htlcswitch.ChannelLinkConfig{ - Peer: p, - DecodeHopIterators: p.server.sphinx.DecodeHopIterators, - ExtractErrorEncrypter: p.server.sphinx.ExtractErrorEncrypter, - FetchLastChannelUpdate: p.server.fetchLastChanUpdate(), - HodlMask: p.cfg.Hodl.Mask(), - Registry: p.server.invoices, - Switch: p.server.htlcSwitch, - Circuits: p.server.htlcSwitch.CircuitModifier(), - ForwardPackets: p.server.interceptableSwitch.ForwardPackets, - FwrdingPolicy: *forwardingPolicy, - FeeEstimator: p.server.cc.feeEstimator, - PreimageCache: p.server.witnessBeacon, - ChainEvents: chainEvents, - UpdateContractSignals: func(signals *contractcourt.ContractSignals) error { - return p.server.chainArb.UpdateContractSignals( - *chanPoint, signals, - ) - }, + Peer: p, + DecodeHopIterators: p.cfg.Sphinx.DecodeHopIterators, + ExtractErrorEncrypter: p.cfg.Sphinx.ExtractErrorEncrypter, + FetchLastChannelUpdate: p.cfg.FetchLastChanUpdate, + HodlMask: p.cfg.Hodl.Mask(), + Registry: p.cfg.Invoices, + Switch: p.cfg.Switch, + Circuits: p.cfg.Switch.CircuitModifier(), + ForwardPackets: p.cfg.InterceptSwitch.ForwardPackets, + FwrdingPolicy: *forwardingPolicy, + FeeEstimator: p.cfg.FeeEstimator, + PreimageCache: p.cfg.WitnessBeacon, + ChainEvents: chainEvents, + UpdateContractSignals: updateContractSignals, OnChannelFailure: onChannelFailure, SyncStates: syncStates, BatchTicker: ticker.New(50 * time.Millisecond), @@ -679,14 +594,14 @@ func (p *peer) addLink(chanPoint *wire.OutPoint, UnsafeReplay: p.cfg.UnsafeReplay, MinFeeUpdateTimeout: htlcswitch.DefaultMinLinkFeeUpdateTimeout, MaxFeeUpdateTimeout: htlcswitch.DefaultMaxLinkFeeUpdateTimeout, - OutgoingCltvRejectDelta: p.outgoingCltvRejectDelta, - TowerClient: p.server.towerClient, + OutgoingCltvRejectDelta: p.cfg.OutgoingCltvRejectDelta, + TowerClient: p.cfg.TowerClient, MaxOutgoingCltvExpiry: p.cfg.MaxOutgoingCltvExpiry, MaxFeeAllocation: p.cfg.MaxChannelFeeAllocation, - NotifyActiveLink: p.server.channelNotifier.NotifyActiveLinkEvent, - NotifyActiveChannel: p.server.channelNotifier.NotifyActiveChannelEvent, - NotifyInactiveChannel: p.server.channelNotifier.NotifyInactiveChannelEvent, - HtlcNotifier: p.server.htlcNotifier, + NotifyActiveLink: p.cfg.ChannelNotifier.NotifyActiveLinkEvent, + NotifyActiveChannel: p.cfg.ChannelNotifier.NotifyActiveChannelEvent, + NotifyInactiveChannel: p.cfg.ChannelNotifier.NotifyInactiveChannelEvent, + HtlcNotifier: p.cfg.HtlcNotifier, } link := htlcswitch.NewChannelLink(linkCfg, lnChan) @@ -695,17 +610,17 @@ func (p *peer) addLink(chanPoint *wire.OutPoint, // links going by the same channel id. If one is found, we'll shut it // down to ensure that the mailboxes are only ever under the control of // one link. - p.server.htlcSwitch.RemoveLink(link.ChanID()) + p.cfg.Switch.RemoveLink(link.ChanID()) // With the channel link created, we'll now notify the htlc switch so // this channel can be used to dispatch local payments and also // passively forward payments. - return p.server.htlcSwitch.AddLink(link) + return p.cfg.Switch.AddLink(link) } // maybeSendNodeAnn sends our node announcement to the remote peer if at least -// one confirmed advertised channel exists with them. -func (p *peer) maybeSendNodeAnn(channels []*channeldb.OpenChannel) { +// one confirmed public channel exists with them. +func (p *Brontide) maybeSendNodeAnn(channels []*channeldb.OpenChannel) { hasConfirmedPublicChan := false for _, channel := range channels { if channel.IsPending { @@ -722,27 +637,27 @@ func (p *peer) maybeSendNodeAnn(channels []*channeldb.OpenChannel) { return } - ourNodeAnn, err := p.server.genNodeAnnouncement(false) + ourNodeAnn, err := p.cfg.GenNodeAnnouncement(false) if err != nil { - srvrLog.Debugf("Unable to retrieve node announcement: %v", err) + peerLog.Debugf("Unable to retrieve node announcement: %v", err) return } if err := p.SendMessageLazy(false, &ourNodeAnn); err != nil { - srvrLog.Debugf("Unable to resend node announcement to %x: %v", - p.pubKeyBytes, err) + peerLog.Debugf("Unable to resend node announcement to %x: %v", + p.cfg.PubKeyBytes, err) } } // WaitForDisconnect waits until the peer has disconnected. A peer may be -// disconnected if the local or remote side terminating the connection, or an +// disconnected if the local or remote side terminates the connection, or an // irrecoverable protocol error has been encountered. This method will only // begin watching the peer's waitgroup after the ready channel or the peer's // quit channel are signaled. The ready channel should only be signaled if a // call to Start returns no error. Otherwise, if the peer fails to start, // calling Disconnect will signal the quit channel and the method will not // block, since no goroutines were spawned. -func (p *peer) WaitForDisconnect(ready chan struct{}) { +func (p *Brontide) WaitForDisconnect(ready chan struct{}) { select { case <-ready: case <-p.quit: @@ -754,7 +669,7 @@ func (p *peer) WaitForDisconnect(ready chan struct{}) { // Disconnect terminates the connection with the remote peer. Additionally, a // signal is sent to the server and htlcSwitch indicating the resources // allocated to the peer can now be cleaned up. -func (p *peer) Disconnect(reason error) { +func (p *Brontide) Disconnect(reason error) { if !atomic.CompareAndSwapInt32(&p.disconnect, 0, 1) { return } @@ -765,20 +680,20 @@ func (p *peer) Disconnect(reason error) { peerLog.Infof(err.Error()) // Ensure that the TCP connection is properly closed before continuing. - p.conn.Close() + p.cfg.Conn.Close() close(p.quit) } // String returns the string representation of this peer. -func (p *peer) String() string { - return fmt.Sprintf("%x@%s", p.pubKeyBytes, p.conn.RemoteAddr()) +func (p *Brontide) String() string { + return fmt.Sprintf("%x@%s", p.cfg.PubKeyBytes, p.cfg.Conn.RemoteAddr()) } // readNextMessage reads, and returns the next message on the wire along with // any additional raw payload. -func (p *peer) readNextMessage() (lnwire.Message, error) { - noiseConn, ok := p.conn.(*brontide.Conn) +func (p *Brontide) readNextMessage() (lnwire.Message, error) { + noiseConn, ok := p.cfg.Conn.(*brontide.Conn) if !ok { return nil, fmt.Errorf("brontide.Conn required to read messages") } @@ -798,7 +713,7 @@ func (p *peer) readNextMessage() (lnwire.Message, error) { // is message oriented and allows nodes to pad on additional data to // the message stream. var rawMsg []byte - err = p.readPool.Submit(func(buf *buffer.Read) error { + err = p.cfg.ReadPool.Submit(func(buf *buffer.Read) error { // Before reading the body of the message, set the read timeout // accordingly to ensure we don't block other readers using the // pool. We do so only after the task has been scheduled to @@ -813,7 +728,6 @@ func (p *peer) readNextMessage() (lnwire.Message, error) { rawMsg, readErr = noiseConn.ReadNextBody(buf[:pktLen]) return readErr }) - atomic.AddUint64(&p.bytesReceived, uint64(len(rawMsg))) if err != nil { return nil, err @@ -840,7 +754,7 @@ func (p *peer) readNextMessage() (lnwire.Message, error) { type msgStream struct { streamShutdown int32 // To be used atomically. - peer *peer + peer *Brontide apply func(lnwire.Message) @@ -863,7 +777,7 @@ type msgStream struct { // that should be buffered in the internal queue. Callers should set this to a // sane value that avoids blocking unnecessarily, but doesn't allow an // unbounded amount of memory to be allocated to buffer incoming messages. -func newMsgStream(p *peer, startMsg, stopMsg string, bufSize uint32, +func newMsgStream(p *Brontide, startMsg, stopMsg string, bufSize uint32, apply func(lnwire.Message)) *msgStream { stream := &msgStream{ @@ -994,7 +908,9 @@ func (ms *msgStream) AddMsg(msg lnwire.Message) { // waitUntilLinkActive waits until the target link is active and returns a // ChannelLink to pass messages to. It accomplishes this by subscribing to // an ActiveLinkEvent which is emitted by the link when it first starts up. -func waitUntilLinkActive(p *peer, cid lnwire.ChannelID) htlcswitch.ChannelLink { +func waitUntilLinkActive(p *Brontide, + cid lnwire.ChannelID) htlcswitch.ChannelLink { + // Subscribe to receive channel events. // // NOTE: If the link is already active by SubscribeChannelEvents, then @@ -1004,7 +920,7 @@ func waitUntilLinkActive(p *peer, cid lnwire.ChannelID) htlcswitch.ChannelLink { // we will get an ActiveLinkEvent notification and retrieve the link. If // the call to GetLink is before SubscribeChannelEvents, however, there // will be a race condition. - sub, err := p.server.channelNotifier.SubscribeChannelEvents() + sub, err := p.cfg.ChannelNotifier.SubscribeChannelEvents() if err != nil { // If we have a non-nil error, then the server is shutting down and we // can exit here and return nil. This means no message will be delivered @@ -1015,7 +931,7 @@ func waitUntilLinkActive(p *peer, cid lnwire.ChannelID) htlcswitch.ChannelLink { // The link may already be active by this point, and we may have missed the // ActiveLinkEvent. Check if the link exists. - link, _ := p.server.htlcSwitch.GetLink(cid) + link, _ := p.cfg.Switch.GetLink(cid) if link != nil { return link } @@ -1045,7 +961,7 @@ func waitUntilLinkActive(p *peer, cid lnwire.ChannelID) htlcswitch.ChannelLink { // The link shouldn't be nil as we received an // ActiveLinkEvent. If it is nil, we return nil and the // calling function should catch it. - link, _ = p.server.htlcSwitch.GetLink(cid) + link, _ = p.cfg.Switch.GetLink(cid) return link case <-p.quit: @@ -1060,53 +976,58 @@ func waitUntilLinkActive(p *peer, cid lnwire.ChannelID) htlcswitch.ChannelLink { // dispatch a message to a channel before it is fully active. A reference to the // channel this stream forwards to his held in scope to prevent unnecessary // lookups. -func newChanMsgStream(p *peer, cid lnwire.ChannelID) *msgStream { +func newChanMsgStream(p *Brontide, cid lnwire.ChannelID) *msgStream { var chanLink htlcswitch.ChannelLink + apply := func(msg lnwire.Message) { + // This check is fine because if the link no longer exists, it will + // be removed from the activeChannels map and subsequent messages + // shouldn't reach the chan msg stream. + if chanLink == nil { + chanLink = waitUntilLinkActive(p, cid) + + // If the link is still not active and the calling function + // errored out, just return. + if chanLink == nil { + return + } + } + + // In order to avoid unnecessarily delivering message + // as the peer is exiting, we'll check quickly to see + // if we need to exit. + select { + case <-p.quit: + return + default: + } + + chanLink.HandleChannelUpdate(msg) + } + return newMsgStream(p, fmt.Sprintf("Update stream for ChannelID(%x) created", cid[:]), fmt.Sprintf("Update stream for ChannelID(%x) exiting", cid[:]), 1000, - func(msg lnwire.Message) { - // This check is fine because if the link no longer exists, it will - // be removed from the activeChannels map and subsequent messages - // shouldn't reach the chan msg stream. - if chanLink == nil { - chanLink = waitUntilLinkActive(p, cid) - - // If the link is still not active and the calling function - // errored out, just return. - if chanLink == nil { - return - } - } - - // In order to avoid unnecessarily delivering message - // as the peer is exiting, we'll check quickly to see - // if we need to exit. - select { - case <-p.quit: - return - default: - } - - chanLink.HandleChannelUpdate(msg) - }, + apply, ) } // newDiscMsgStream is used to setup a msgStream between the peer and the // authenticated gossiper. This stream should be used to forward all remote // channel announcements. -func newDiscMsgStream(p *peer) *msgStream { - return newMsgStream(p, +func newDiscMsgStream(p *Brontide) *msgStream { + apply := func(msg lnwire.Message) { + p.cfg.AuthGossiper.ProcessRemoteAnnouncement(msg, p) + } + + return newMsgStream( + p, "Update stream for gossiper created", "Update stream for gossiper exited", 1000, - func(msg lnwire.Message) { - p.server.authGossiper.ProcessRemoteAnnouncement(msg, p) - }, + apply, ) } @@ -1114,7 +1035,7 @@ func newDiscMsgStream(p *peer) *msgStream { // properly dispatching the handling of the message to the proper subsystem. // // NOTE: This method MUST be run as a goroutine. -func (p *peer) readHandler() { +func (p *Brontide) readHandler() { defer p.wg.Done() // We'll stop the timer after a new messages is received, and also @@ -1209,15 +1130,15 @@ out: p.queueMsg(lnwire.NewPong(pongBytes), nil) case *lnwire.OpenChannel: - p.server.fundingMgr.processFundingOpen(msg, p) + p.cfg.ProcessFundingOpen(msg, p) case *lnwire.AcceptChannel: - p.server.fundingMgr.processFundingAccept(msg, p) + p.cfg.ProcessFundingAccept(msg, p) case *lnwire.FundingCreated: - p.server.fundingMgr.processFundingCreated(msg, p) + p.cfg.ProcessFundingCreated(msg, p) case *lnwire.FundingSigned: - p.server.fundingMgr.processFundingSigned(msg, p) + p.cfg.ProcessFundingSigned(msg, p) case *lnwire.FundingLocked: - p.server.fundingMgr.processFundingLocked(msg, p) + p.cfg.ProcessFundingLocked(msg, p) case *lnwire.Shutdown: select { @@ -1310,7 +1231,7 @@ out: // isActiveChannel returns true if the provided channel id is active, otherwise // returns false. -func (p *peer) isActiveChannel(chanID lnwire.ChannelID) bool { +func (p *Brontide) isActiveChannel(chanID lnwire.ChannelID) bool { p.activeChanMtx.RLock() _, ok := p.activeChannels[chanID] p.activeChanMtx.RUnlock() @@ -1319,9 +1240,9 @@ func (p *peer) isActiveChannel(chanID lnwire.ChannelID) bool { // storeError stores an error in our peer's buffer of recent errors with the // current timestamp. Errors are only stored if we have at least one active -// channel with the peer to mitigate dos attack vectors where a peer costlessly +// channel with the peer to mitigate a dos vector where a peer costlessly // connects to us and spams us with errors. -func (p *peer) storeError(err error) { +func (p *Brontide) storeError(err error) { var haveChannels bool p.activeChanMtx.RLock() @@ -1343,8 +1264,8 @@ func (p *peer) storeError(err error) { return } - p.errorBuffer.Add( - ×tampedError{timestamp: time.Now(), error: err}, + p.cfg.ErrorBuffer.Add( + &TimestampedError{Timestamp: time.Now(), Error: err}, ) } @@ -1354,8 +1275,8 @@ func (p *peer) storeError(err error) { // open with the peer. // // NOTE: This method should only be called from within the readHandler. -func (p *peer) handleError(msg *lnwire.Error) bool { - key := p.addr.IdentityKey +func (p *Brontide) handleError(msg *lnwire.Error) bool { + key := p.cfg.Addr.IdentityKey // Store the error we have received. p.storeError(msg) @@ -1372,8 +1293,8 @@ func (p *peer) handleError(msg *lnwire.Error) bool { // If the channel ID for the error message corresponds to a pending // channel, then the funding manager will handle the error. - case p.server.fundingMgr.IsPendingChannel(msg.ChanID, key): - p.server.fundingMgr.processFundingError(msg, key) + case p.cfg.IsPendingChannel(msg.ChanID, key): + p.cfg.ProcessFundingError(msg, key) return false // If not we hand the error to the channel link for this channel. @@ -1522,7 +1443,7 @@ func messageSummary(msg lnwire.Message) string { // less spammy log messages in trace mode by setting the 'Curve" parameter to // nil. Doing this avoids printing out each of the field elements in the curve // parameters for secp256k1. -func (p *peer) logWireMessage(msg lnwire.Message, read bool) { +func (p *Brontide) logWireMessage(msg lnwire.Message, read bool) { summaryPrefix := "Received" if !read { summaryPrefix = "Sending" @@ -1581,10 +1502,10 @@ func (p *peer) logWireMessage(msg lnwire.Message, read bool) { // writeMessage writes and flushes the target lnwire.Message to the remote peer. // If the passed message is nil, this method will only try to flush an existing -// message buffered on the connection. It is safe to recall this method with a -// nil message iff a timeout error is returned. This will continue to flush the -// pending message to the wire. -func (p *peer) writeMessage(msg lnwire.Message) error { +// message buffered on the connection. It is safe to call this method again +// with a nil message iff a timeout error is returned. This will continue to +// flush the pending message to the wire. +func (p *Brontide) writeMessage(msg lnwire.Message) error { // Simply exit if we're shutting down. if atomic.LoadInt32(&p.disconnect) != 0 { return lnpeer.ErrPeerExiting @@ -1595,7 +1516,7 @@ func (p *peer) writeMessage(msg lnwire.Message) error { p.logWireMessage(msg, false) } - noiseConn, ok := p.conn.(*brontide.Conn) + noiseConn, ok := p.cfg.Conn.(*brontide.Conn) if !ok { return fmt.Errorf("brontide.Conn required to write messages") } @@ -1631,7 +1552,7 @@ func (p *peer) writeMessage(msg lnwire.Message) error { // Otherwise, this is a new message. We'll acquire a write buffer to // serialize the message and buffer the ciphertext on the connection. - err := p.writePool.Submit(func(buf *bytes.Buffer) error { + err := p.cfg.WritePool.Submit(func(buf *bytes.Buffer) error { // Using a buffer allocated by the write pool, encode the // message directly into the buffer. _, writeErr := lnwire.WriteMessage(buf, msg, 0) @@ -1658,7 +1579,7 @@ func (p *peer) writeMessage(msg lnwire.Message) error { // drained. // // NOTE: This method MUST be run as a goroutine. -func (p *peer) writeHandler() { +func (p *Brontide) writeHandler() { // We'll stop the timer after a new messages is sent, and also reset it // after we process the next message. idleTimer := time.AfterFunc(idleTimeout, func() { @@ -1751,7 +1672,7 @@ out: // to be eventually sent out on the wire by the writeHandler. // // NOTE: This method MUST be run as a goroutine. -func (p *peer) queueHandler() { +func (p *Brontide) queueHandler() { defer p.wg.Done() // priorityMsgs holds an in order list of messages deemed high-priority @@ -1819,7 +1740,7 @@ func (p *peer) queueHandler() { // connection is still active. // // NOTE: This method MUST be run as a goroutine. -func (p *peer) pingHandler() { +func (p *Brontide) pingHandler() { defer p.wg.Done() pingTicker := time.NewTicker(pingInterval) @@ -1840,32 +1761,35 @@ out: } // PingTime returns the estimated ping time to the peer in microseconds. -func (p *peer) PingTime() int64 { +func (p *Brontide) PingTime() int64 { return atomic.LoadInt64(&p.pingTime) } // queueMsg adds the lnwire.Message to the back of the high priority send queue. // If the errChan is non-nil, an error is sent back if the msg failed to queue // or failed to write, and nil otherwise. -func (p *peer) queueMsg(msg lnwire.Message, errChan chan error) { +func (p *Brontide) queueMsg(msg lnwire.Message, errChan chan error) { p.queue(true, msg, errChan) } // queueMsgLazy adds the lnwire.Message to the back of the low priority send // queue. If the errChan is non-nil, an error is sent back if the msg failed to // queue or failed to write, and nil otherwise. -func (p *peer) queueMsgLazy(msg lnwire.Message, errChan chan error) { +func (p *Brontide) queueMsgLazy(msg lnwire.Message, errChan chan error) { p.queue(false, msg, errChan) } // queue sends a given message to the queueHandler using the passed priority. If // the errChan is non-nil, an error is sent back if the msg failed to queue or // failed to write, and nil otherwise. -func (p *peer) queue(priority bool, msg lnwire.Message, errChan chan error) { +func (p *Brontide) queue(priority bool, msg lnwire.Message, + errChan chan error) { + select { case p.outgoingQueue <- outgoingMsg{priority, msg, errChan}: case <-p.quit: - peerLog.Tracef("Peer shutting down, could not enqueue msg.") + peerLog.Tracef("Peer shutting down, could not enqueue msg: %v.", + spew.Sdump(msg)) if errChan != nil { errChan <- lnpeer.ErrPeerExiting } @@ -1874,7 +1798,7 @@ func (p *peer) queue(priority bool, msg lnwire.Message, errChan chan error) { // ChannelSnapshots returns a slice of channel snapshots detailing all // currently active channels maintained with the remote peer. -func (p *peer) ChannelSnapshots() []*channeldb.ChannelSnapshot { +func (p *Brontide) ChannelSnapshots() []*channeldb.ChannelSnapshot { p.activeChanMtx.RLock() defer p.activeChanMtx.RUnlock() @@ -1900,8 +1824,8 @@ func (p *peer) ChannelSnapshots() []*channeldb.ChannelSnapshot { // genDeliveryScript returns a new script to be used to send our funds to in // the case of a cooperative channel close negotiation. -func (p *peer) genDeliveryScript() ([]byte, error) { - deliveryAddr, err := p.server.cc.wallet.NewAddress( +func (p *Brontide) genDeliveryScript() ([]byte, error) { + deliveryAddr, err := p.cfg.Wallet.NewAddress( lnwallet.WitnessPubKey, false, ) if err != nil { @@ -1918,13 +1842,13 @@ func (p *peer) genDeliveryScript() ([]byte, error) { // channels maintained with the remote peer. // // NOTE: This method MUST be run as a goroutine. -func (p *peer) channelManager() { +func (p *Brontide) channelManager() { defer p.wg.Done() // reenableTimeout will fire once after the configured channel status // interval has elapsed. This will trigger us to sign new channel // updates and broadcast them with the "disabled" flag unset. - reenableTimeout := time.After(p.chanActiveTimeout) + reenableTimeout := time.After(p.cfg.ChanActiveTimeout) out: for { @@ -1976,7 +1900,7 @@ out: // set of active channels, so we can look it up later // easily according to its channel ID. lnChan, err := lnwallet.NewLightningChannel( - p.server.cc.signer, newChan, p.server.sigPool, + p.cfg.Signer, newChan, p.cfg.SigPool, ) if err != nil { p.activeChanMtx.Unlock() @@ -2001,16 +1925,7 @@ out: // necessary items it needs to function. // // TODO(roasbeef): panic on below? - _, currentHeight, err := p.server.cc.chainIO.GetBestBlock() - if err != nil { - err := fmt.Errorf("unable to get best "+ - "block: %v", err) - peerLog.Errorf(err.Error()) - - newChanReq.err <- err - continue - } - chainEvents, err := p.server.chainArb.SubscribeChannelEvents( + chainEvents, err := p.cfg.ChainArb.SubscribeChannelEvents( *chanPoint, ) if err != nil { @@ -2029,7 +1944,7 @@ out: // at initial channel creation. Note that the maximum HTLC value // defaults to the cap on the total value of outstanding HTLCs. fwdMinHtlc := lnChan.FwdMinHtlc() - defaultPolicy := p.server.cc.routingPolicy + defaultPolicy := p.cfg.RoutingPolicy forwardingPolicy := &htlcswitch.ForwardingPolicy{ MinHTLCOut: fwdMinHtlc, MaxHTLC: newChan.LocalChanCfg.MaxPendingAmount, @@ -2048,7 +1963,7 @@ out: // Create the link and add it to the switch. err = p.addLink( chanPoint, lnChan, forwardingPolicy, - chainEvents, currentHeight, shouldReestablish, + chainEvents, shouldReestablish, ) if err != nil { err := fmt.Errorf("can't register new channel "+ @@ -2079,69 +1994,7 @@ out: // message from the remote peer, we'll use this message to // advance the chan closer state machine. case closeMsg := <-p.chanCloseMsgs: - // We'll now fetch the matching closing state machine - // in order to continue, or finalize the channel - // closure process. - chanCloser, err := p.fetchActiveChanCloser(closeMsg.cid) - if err != nil { - // If the channel is not known to us, we'll - // simply ignore this message. - if err == ErrChannelNotFound { - continue - } - - peerLog.Errorf("Unable to respond to remote "+ - "close msg: %v", err) - - errMsg := &lnwire.Error{ - ChanID: closeMsg.cid, - Data: lnwire.ErrorData(err.Error()), - } - p.queueMsg(errMsg, nil) - continue - } - - // Next, we'll process the next message using the - // target state machine. We'll either continue - // negotiation, or halt. - msgs, closeFin, err := chanCloser.ProcessCloseMsg( - closeMsg.msg, - ) - if err != nil { - err := fmt.Errorf("unable to process close "+ - "msg: %v", err) - peerLog.Error(err) - - // As the negotiations failed, we'll reset the - // channel state to ensure we act to on-chain - // events as normal. - chanCloser.Channel().ResetState() - - if chanCloser.CloseRequest() != nil { - chanCloser.CloseRequest().Err <- err - } - delete(p.activeChanCloses, closeMsg.cid) - continue - } - - // Queue any messages to the remote peer that need to - // be sent as a part of this latest round of - // negotiations. - for _, msg := range msgs { - p.queueMsg(msg, nil) - } - - // If we haven't finished close negotiations, then - // we'll continue as we can't yet finalize the closure. - if !closeFin { - continue - } - - // Otherwise, we've agreed on a closing fee! In this - // case, we'll wrap up the channel closure by notifying - // relevant sub-systems and launching a goroutine to - // wait for close tx conf. - p.finalizeChanClosure(chanCloser) + p.handleCloseMsg(closeMsg) // The channel reannounce delay has elapsed, broadcast the // reenabled channel updates to the network. This should only @@ -2162,7 +2015,6 @@ out: reenableTimeout = nil case <-p.quit: - // As, we've been signalled to exit, we'll reset all // our active channel back to their default state. p.activeChanMtx.Lock() @@ -2185,7 +2037,7 @@ out: // peer, and reenables each public, non-pending channel. This is done at the // gossip level by broadcasting a new ChannelUpdate with the disabled bit unset. // No message will be sent if the channel is already enabled. -func (p *peer) reenableActiveChannels() { +func (p *Brontide) reenableActiveChannels() { // First, filter all known channels with this peer for ones that are // both public and not pending. var activePublicChans []wire.OutPoint @@ -2221,9 +2073,9 @@ func (p *peer) reenableActiveChannels() { // disabled bit to false and send out a new ChannelUpdate. If this // channel is already active, the update won't be sent. for _, chanPoint := range activePublicChans { - err := p.server.chanStatusMgr.RequestEnable(chanPoint) + err := p.cfg.ChanStatusMgr.RequestEnable(chanPoint) if err != nil { - srvrLog.Errorf("Unable to enable channel %v: %v", + peerLog.Errorf("Unable to enable channel %v: %v", chanPoint, err) } } @@ -2233,7 +2085,7 @@ func (p *peer) reenableActiveChannels() { // for the target channel ID. If the channel isn't active an error is returned. // Otherwise, either an existing state machine will be returned, or a new one // will be created. -func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) ( +func (p *Brontide) fetchActiveChanCloser(chanID lnwire.ChannelID) ( *chancloser.ChanCloser, error) { // First, we'll ensure that we actually know of the target channel. If @@ -2278,14 +2130,14 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) ( // In order to begin fee negotiations, we'll first compute our // target ideal fee-per-kw. We'll set this to a lax value, as // we weren't the ones that initiated the channel closure. - feePerKw, err := p.server.cc.feeEstimator.EstimateFeePerKW(6) + feePerKw, err := p.cfg.FeeEstimator.EstimateFeePerKW(6) if err != nil { peerLog.Errorf("unable to query fee estimator: %v", err) return nil, fmt.Errorf("unable to estimate fee") } - _, startingHeight, err := p.server.cc.chainIO.GetBestBlock() + _, startingHeight, err := p.cfg.ChainIO.GetBestBlock() if err != nil { peerLog.Errorf("unable to obtain best block: %v", err) return nil, fmt.Errorf("cannot obtain best block") @@ -2294,11 +2146,11 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) ( chanCloser = chancloser.NewChanCloser( chancloser.ChanCloseCfg{ Channel: channel, - UnregisterChannel: p.server.htlcSwitch.RemoveLink, - BroadcastTx: p.server.cc.wallet.PublishTransaction, - DisableChannel: p.server.chanStatusMgr.RequestDisable, + UnregisterChannel: p.cfg.Switch.RemoveLink, + BroadcastTx: p.cfg.Wallet.PublishTransaction, + DisableChannel: p.cfg.ChanStatusMgr.RequestDisable, Disconnect: func() error { - return p.server.DisconnectPeer(p.IdentityKey()) + return p.cfg.DisconnectPeer(p.IdentityKey()) }, Quit: p.quit, }, @@ -2347,7 +2199,7 @@ func chooseDeliveryScript(upfront, // handleLocalCloseReq kicks-off the workflow to execute a cooperative or // forced unilateral closure of the channel initiated by a local subsystem. -func (p *peer) handleLocalCloseReq(req *htlcswitch.ChanClose) { +func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) { chanID := lnwire.NewChanIDFromOutPoint(req.ChanPoint) p.activeChanMtx.RLock() @@ -2400,7 +2252,7 @@ func (p *peer) handleLocalCloseReq(req *htlcswitch.ChanClose) { // Next, we'll create a new channel closer state machine to // handle the close negotiation. - _, startingHeight, err := p.server.cc.chainIO.GetBestBlock() + _, startingHeight, err := p.cfg.ChainIO.GetBestBlock() if err != nil { peerLog.Errorf(err.Error()) req.Err <- err @@ -2410,11 +2262,11 @@ func (p *peer) handleLocalCloseReq(req *htlcswitch.ChanClose) { chanCloser := chancloser.NewChanCloser( chancloser.ChanCloseCfg{ Channel: channel, - UnregisterChannel: p.server.htlcSwitch.RemoveLink, - BroadcastTx: p.server.cc.wallet.PublishTransaction, - DisableChannel: p.server.chanStatusMgr.RequestDisable, + UnregisterChannel: p.cfg.Switch.RemoveLink, + BroadcastTx: p.cfg.Wallet.PublishTransaction, + DisableChannel: p.cfg.ChanStatusMgr.RequestDisable, Disconnect: func() error { - return p.server.DisconnectPeer(p.IdentityKey()) + return p.cfg.DisconnectPeer(p.IdentityKey()) }, Quit: p.quit, }, @@ -2453,10 +2305,10 @@ func (p *peer) handleLocalCloseReq(req *htlcswitch.ChanClose) { } } -// linkFailureReport is sent to the channelManager whenever a link that was -// added to the switch reports a link failure, and is forced to exit. The report -// houses the necessary information to cleanup the channel state, send back the -// error message, and force close if necessary. +// linkFailureReport is sent to the channelManager whenever a link reports a +// link failure, and is forced to exit. The report houses the necessary +// information to clean up the channel state, send back the error message, and +// force close if necessary. type linkFailureReport struct { chanPoint wire.OutPoint chanID lnwire.ChannelID @@ -2465,10 +2317,10 @@ type linkFailureReport struct { } // handleLinkFailure processes a link failure report when a link in the switch -// fails. It handles facilitates removal of all channel state within the peer, +// fails. It facilitates the removal of all channel state within the peer, // force closing the channel depending on severity, and sending the error // message back to the remote party. -func (p *peer) handleLinkFailure(failure linkFailureReport) { +func (p *Brontide) handleLinkFailure(failure linkFailureReport) { // We begin by wiping the link, which will remove it from the switch, // such that it won't be attempted used for any more updates. // @@ -2484,7 +2336,7 @@ func (p *peer) handleLinkFailure(failure linkFailureReport) { peerLog.Warnf("Force closing link(%v)", failure.shortChanID) - closeTx, err := p.server.chainArb.ForceCloseContract( + closeTx, err := p.cfg.ChainArb.ForceCloseContract( failure.chanPoint, ) if err != nil { @@ -2523,7 +2375,7 @@ func (p *peer) handleLinkFailure(failure linkFailureReport) { // machine should be passed in. Once the transaction has been sufficiently // confirmed, the channel will be marked as fully closed within the database, // and any clients will be notified of updates to the closing state. -func (p *peer) finalizeChanClosure(chanCloser *chancloser.ChanCloser) { +func (p *Brontide) finalizeChanClosure(chanCloser *chancloser.ChanCloser) { closeReq := chanCloser.CloseRequest() // First, we'll clear all indexes related to the channel in question. @@ -2533,7 +2385,7 @@ func (p *peer) finalizeChanClosure(chanCloser *chancloser.ChanCloser) { // Next, we'll launch a goroutine which will request to be notified by // the ChainNotifier once the closure transaction obtains a single // confirmation. - notifier := p.server.cc.chainNotifier + notifier := p.cfg.ChainNotifier // If any error happens during waitForChanToClose, forward it to // closeReq. If this channel closure is not locally initiated, closeReq @@ -2556,18 +2408,18 @@ func (p *peer) finalizeChanClosure(chanCloser *chancloser.ChanCloser) { // If this is a locally requested shutdown, update the caller with a // new event detailing the current pending state of this request. if closeReq != nil { - closeReq.Updates <- &pendingUpdate{ + closeReq.Updates <- &PendingUpdate{ Txid: closingTxid[:], } } - go waitForChanToClose(chanCloser.NegotiationHeight(), notifier, errChan, + go WaitForChanToClose(chanCloser.NegotiationHeight(), notifier, errChan, chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() { // Respond to the local subsystem which requested the // channel closure. if closeReq != nil { - closeReq.Updates <- &channelCloseUpdate{ + closeReq.Updates <- &ChannelCloseUpdate{ ClosingTxid: closingTxid[:], Success: true, } @@ -2575,12 +2427,12 @@ func (p *peer) finalizeChanClosure(chanCloser *chancloser.ChanCloser) { }) } -// waitForChanToClose uses the passed notifier to wait until the channel has +// WaitForChanToClose uses the passed notifier to wait until the channel has // been detected as closed on chain and then concludes by executing the // following actions: the channel point will be sent over the settleChan, and // finally the callback will be executed. If any error is encountered within // the function, then it will be sent over the errChan. -func waitForChanToClose(bestHeight uint32, notifier chainntnfs.ChainNotifier, +func WaitForChanToClose(bestHeight uint32, notifier chainntnfs.ChainNotifier, errChan chan error, chanPoint *wire.OutPoint, closingTxID *chainhash.Hash, closeScript []byte, cb func()) { @@ -2617,8 +2469,8 @@ func waitForChanToClose(bestHeight uint32, notifier chainntnfs.ChainNotifier, } // WipeChannel removes the passed channel point from all indexes associated with -// the peer, and the switch. -func (p *peer) WipeChannel(chanPoint *wire.OutPoint) { +// the peer and the switch. +func (p *Brontide) WipeChannel(chanPoint *wire.OutPoint) { chanID := lnwire.NewChanIDFromOutPoint(chanPoint) p.activeChanMtx.Lock() @@ -2627,12 +2479,12 @@ func (p *peer) WipeChannel(chanPoint *wire.OutPoint) { // Instruct the HtlcSwitch to close this link as the channel is no // longer active. - p.server.htlcSwitch.RemoveLink(chanID) + p.cfg.Switch.RemoveLink(chanID) } // handleInitMsg handles the incoming init message which contains global and -// local features vectors. If feature vectors are incompatible then disconnect. -func (p *peer) handleInitMsg(msg *lnwire.Init) error { +// local feature vectors. If feature vectors are incompatible then disconnect. +func (p *Brontide) handleInitMsg(msg *lnwire.Init) error { // First, merge any features from the legacy global features field into // those presented in the local features fields. err := msg.Features.Merge(msg.GlobalFeatures) @@ -2641,7 +2493,7 @@ func (p *peer) handleInitMsg(msg *lnwire.Init) error { err) } - // Then, finalize the remote feature vector providing the flatteneed + // Then, finalize the remote feature vector providing the flattened // feature bit namespace. p.remoteFeatures = lnwire.NewFeatureVector( msg.Features, lnwire.Features, @@ -2654,8 +2506,8 @@ func (p *peer) handleInitMsg(msg *lnwire.Init) error { return fmt.Errorf("invalid remote features: %v", err) } - // Ensure the remote party's feature vector contains all transistive - // dependencies. We know ours are are correct since they are validated + // Ensure the remote party's feature vector contains all transitive + // dependencies. We know ours are correct since they are validated // during the feature manager's instantiation. err = feature.ValidateDeps(p.remoteFeatures) if err != nil { @@ -2664,8 +2516,7 @@ func (p *peer) handleInitMsg(msg *lnwire.Init) error { // Now that we know we understand their requirements, we'll check to // see if they don't support anything that we deem to be mandatory. - switch { - case !p.remoteFeatures.HasFeature(lnwire.DataLossProtectRequired): + if !p.remoteFeatures.HasFeature(lnwire.DataLossProtectRequired) { return fmt.Errorf("data loss protection required") } @@ -2677,8 +2528,8 @@ func (p *peer) handleInitMsg(msg *lnwire.Init) error { // behavior off the set of negotiated feature bits. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) LocalFeatures() *lnwire.FeatureVector { - return p.features +func (p *Brontide) LocalFeatures() *lnwire.FeatureVector { + return p.cfg.Features } // RemoteFeatures returns the set of global features that has been advertised by @@ -2686,16 +2537,16 @@ func (p *peer) LocalFeatures() *lnwire.FeatureVector { // their behavior off the set of negotiated feature bits. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) RemoteFeatures() *lnwire.FeatureVector { +func (p *Brontide) RemoteFeatures() *lnwire.FeatureVector { return p.remoteFeatures } -// sendInitMsg sends init message to remote peer which contains our currently -// supported local and global features. -func (p *peer) sendInitMsg() error { +// sendInitMsg sends the Init message to the remote peer. This message contains our +// currently supported local and global features. +func (p *Brontide) sendInitMsg() error { msg := lnwire.NewInitMessage( - p.legacyFeatures.RawFeatureVector, - p.features.RawFeatureVector, + p.cfg.LegacyFeatures.RawFeatureVector, + p.cfg.Features.RawFeatureVector, ) return p.writeMessage(msg) @@ -2703,7 +2554,7 @@ func (p *peer) sendInitMsg() error { // resendChanSyncMsg will attempt to find a channel sync message for the closed // channel and resend it to our peer. -func (p *peer) resendChanSyncMsg(cid lnwire.ChannelID) error { +func (p *Brontide) resendChanSyncMsg(cid lnwire.ChannelID) error { // If we already re-sent the mssage for this channel, we won't do it // again. if _, ok := p.resentChanSyncMsg[cid]; ok { @@ -2711,7 +2562,7 @@ func (p *peer) resendChanSyncMsg(cid lnwire.ChannelID) error { } // Check if we have any channel sync messages stored for this channel. - c, err := p.server.chanDB.FetchClosedChannelForID(cid) + c, err := p.cfg.ChannelDB.FetchClosedChannelForID(cid) if err != nil { return fmt.Errorf("unable to fetch channel sync messages for "+ "peer %v: %v", p, err) @@ -2745,23 +2596,23 @@ func (p *peer) resendChanSyncMsg(cid lnwire.ChannelID) error { return nil } -// SendMessage sends a variadic number of high-priority message to remote peer. -// The first argument denotes if the method should block until the messages have -// been sent to the remote peer or an error is returned, otherwise it returns -// immediately after queuing. +// SendMessage sends a variadic number of high-priority messages to the remote +// peer. The first argument denotes if the method should block until the +// messages have been sent to the remote peer or an error is returned, +// otherwise it returns immediately after queuing. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) SendMessage(sync bool, msgs ...lnwire.Message) error { +func (p *Brontide) SendMessage(sync bool, msgs ...lnwire.Message) error { return p.sendMessage(sync, true, msgs...) } -// SendMessageLazy sends a variadic number of low-priority message to remote -// peer. The first argument denotes if the method should block until the -// messages have been sent to the remote peer or an error is returned, otherwise -// it returns immediately after queueing. +// SendMessageLazy sends a variadic number of low-priority messages to the +// remote peer. The first argument denotes if the method should block until +// the messages have been sent to the remote peer or an error is returned, +// otherwise it returns immediately after queueing. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error { +func (p *Brontide) SendMessageLazy(sync bool, msgs ...lnwire.Message) error { return p.sendMessage(sync, false, msgs...) } @@ -2769,7 +2620,7 @@ func (p *peer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error { // to the remote peer. If sync is true, this method will block until the // messages have been sent to the remote peer or an error is returned, otherwise // it returns immediately after queueing. -func (p *peer) sendMessage(sync, priority bool, msgs ...lnwire.Message) error { +func (p *Brontide) sendMessage(sync, priority bool, msgs ...lnwire.Message) error { // Add all incoming messages to the outgoing queue. A list of error // chans is populated for each message if the caller requested a sync // send. @@ -2801,7 +2652,7 @@ func (p *peer) sendMessage(sync, priority bool, msgs ...lnwire.Message) error { return err case <-p.quit: return lnpeer.ErrPeerExiting - case <-p.server.quit: + case <-p.cfg.Quit: return lnpeer.ErrPeerExiting } } @@ -2812,29 +2663,29 @@ func (p *peer) sendMessage(sync, priority bool, msgs ...lnwire.Message) error { // PubKey returns the pubkey of the peer in compressed serialized format. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) PubKey() [33]byte { - return p.pubKeyBytes +func (p *Brontide) PubKey() [33]byte { + return p.cfg.PubKeyBytes } // IdentityKey returns the public key of the remote peer. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) IdentityKey() *btcec.PublicKey { - return p.addr.IdentityKey +func (p *Brontide) IdentityKey() *btcec.PublicKey { + return p.cfg.Addr.IdentityKey } // Address returns the network address of the remote peer. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) Address() net.Addr { - return p.addr.Address +func (p *Brontide) Address() net.Addr { + return p.cfg.Addr.Address } // AddNewChannel adds a new channel to the peer. The channel should fail to be // added if the cancel channel is closed. // // NOTE: Part of the lnpeer.Peer interface. -func (p *peer) AddNewChannel(channel *channeldb.OpenChannel, +func (p *Brontide) AddNewChannel(channel *channeldb.OpenChannel, cancel <-chan struct{}) error { errChan := make(chan error, 1) @@ -2863,16 +2714,126 @@ func (p *peer) AddNewChannel(channel *channeldb.OpenChannel, // StartTime returns the time at which the connection was established if the // peer started successfully, and zero otherwise. -func (p *peer) StartTime() time.Time { +func (p *Brontide) StartTime() time.Time { return p.startTime } -// LinkUpdater is an interface implemented by most messages in BOLT 2 that are -// allowed to update the channel state. -type LinkUpdater interface { - // TargetChanID returns the channel id of the link for which this - // message is intended. - TargetChanID() lnwire.ChannelID +// handleCloseMsg is called when a new cooperative channel closure related +// message is received from the remote peer. We'll use this message to advance +// the chan closer state machine. +func (p *Brontide) handleCloseMsg(msg *closeMsg) { + // We'll now fetch the matching closing state machine in order to continue, + // or finalize the channel closure process. + chanCloser, err := p.fetchActiveChanCloser(msg.cid) + if err != nil { + // If the channel is not known to us, we'll simply ignore this message. + if err == ErrChannelNotFound { + return + } + + peerLog.Errorf("Unable to respond to remote close msg: %v", err) + + errMsg := &lnwire.Error{ + ChanID: msg.cid, + Data: lnwire.ErrorData(err.Error()), + } + p.queueMsg(errMsg, nil) + return + } + + // Next, we'll process the next message using the target state machine. + // We'll either continue negotiation, or halt. + msgs, closeFin, err := chanCloser.ProcessCloseMsg( + msg.msg, + ) + if err != nil { + err := fmt.Errorf("unable to process close msg: %v", err) + peerLog.Error(err) + + // As the negotiations failed, we'll reset the channel state machine to + // ensure we act to on-chain events as normal. + chanCloser.Channel().ResetState() + + if chanCloser.CloseRequest() != nil { + chanCloser.CloseRequest().Err <- err + } + delete(p.activeChanCloses, msg.cid) + return + } + + // Queue any messages to the remote peer that need to be sent as a part of + // this latest round of negotiations. + for _, msg := range msgs { + p.queueMsg(msg, nil) + } + + // If we haven't finished close negotiations, then we'll continue as we + // can't yet finalize the closure. + if !closeFin { + return + } + + // Otherwise, we've agreed on a closing fee! In this case, we'll wrap up + // the channel closure by notifying relevant sub-systems and launching a + // goroutine to wait for close tx conf. + p.finalizeChanClosure(chanCloser) } -// TODO(roasbeef): make all start/stop mutexes a CAS +// HandleLocalCloseChanReqs accepts a *htlcswitch.ChanClose and passes it onto +// the channelManager goroutine, which will shut down the link and possibly +// close the channel. +func (p *Brontide) HandleLocalCloseChanReqs(req *htlcswitch.ChanClose) { + select { + case p.localCloseChanReqs <- req: + peerLog.Infof("Local close channel request delivered to peer: %v", + p.PubKey()) + case <-p.quit: + peerLog.Infof("Unable to deliver local close channel request to peer "+ + "%x", p.PubKey()) + } +} + +// NetAddress returns the network of the remote peer as an lnwire.NetAddress. +func (p *Brontide) NetAddress() *lnwire.NetAddress { + return p.cfg.Addr +} + +// Inbound is a getter for the Brontide's Inbound boolean in cfg. +func (p *Brontide) Inbound() bool { + return p.cfg.Inbound +} + +// ConnReq is a getter for the Brontide's connReq in cfg. +func (p *Brontide) ConnReq() *connmgr.ConnReq { + return p.cfg.ConnReq +} + +// ErrorBuffer is a getter for the Brontide's errorBuffer in cfg. +func (p *Brontide) ErrorBuffer() *queue.CircularBuffer { + return p.cfg.ErrorBuffer +} + +// SetAddress sets the remote peer's address given an address. +func (p *Brontide) SetAddress(address net.Addr) { + p.cfg.Addr.Address = address +} + +// ActiveSignal returns the peer's active signal. +func (p *Brontide) ActiveSignal() chan struct{} { + return p.activeSignal +} + +// Conn returns a pointer to the peer's connection struct. +func (p *Brontide) Conn() net.Conn { + return p.cfg.Conn +} + +// BytesReceived returns the number of bytes received from the peer. +func (p *Brontide) BytesReceived() uint64 { + return atomic.LoadUint64(&p.bytesReceived) +} + +// BytesSent returns the number of bytes sent to the peer. +func (p *Brontide) BytesSent() uint64 { + return atomic.LoadUint64(&p.bytesSent) +} diff --git a/peer_test.go b/peer/brontide_test.go similarity index 61% rename from peer_test.go rename to peer/brontide_test.go index 48e2a473d..78800b115 100644 --- a/peer_test.go +++ b/peer/brontide_test.go @@ -1,19 +1,17 @@ -// +build !rpctest - -package lnd +package peer import ( "bytes" "testing" "time" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/htlcswitch" - "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chancloser" "github.com/lightningnetwork/lnd/lnwire" ) @@ -35,12 +33,12 @@ var ( func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) { t.Parallel() - notifier := &mockNotfier{ + notifier := &mockNotifier{ confChannel: make(chan *chainntnfs.TxConfirmation), } broadcastTxChan := make(chan *wire.MsgTx) - responder, responderChan, initiatorChan, cleanUp, err := createTestPeer( + alicePeer, bobChan, cleanUp, err := createTestPeer( notifier, broadcastTxChan, noUpdate, ) if err != nil { @@ -48,19 +46,19 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) { } defer cleanUp() - chanID := lnwire.NewChanIDFromOutPoint(responderChan.ChannelPoint()) + chanID := lnwire.NewChanIDFromOutPoint(bobChan.ChannelPoint()) // We send a shutdown request to Alice. She will now be the responding // node in this shutdown procedure. We first expect Alice to answer // this shutdown request with a Shutdown message. - responder.chanCloseMsgs <- &closeMsg{ + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: lnwire.NewShutdown(chanID, dummyDeliveryScript), } var msg lnwire.Message select { - case outMsg := <-responder.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive shutdown message") @@ -73,49 +71,61 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) { respDeliveryScript := shutdownMsg.Address - // Alice will thereafter send a ClosingSigned message, indicating her - // proposed closing transaction fee. + // Alice will then send a ClosingSigned message, indicating her proposed + // closing transaction fee. Alice sends the ClosingSigned message as she is + // the initiator of the channel. select { - case outMsg := <-responder.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive ClosingSigned message") } - responderClosingSigned, ok := msg.(*lnwire.ClosingSigned) + respClosingSigned, ok := msg.(*lnwire.ClosingSigned) if !ok { t.Fatalf("expected ClosingSigned message, got %T", msg) } // We accept the fee, and send a ClosingSigned with the same fee back, // so she knows we agreed. - peerFee := responderClosingSigned.FeeSatoshis - initiatorSig, _, _, err := initiatorChan.CreateCloseProposal( - peerFee, dummyDeliveryScript, respDeliveryScript, + aliceFee := respClosingSigned.FeeSatoshis + bobSig, _, _, err := bobChan.CreateCloseProposal( + aliceFee, dummyDeliveryScript, respDeliveryScript, ) if err != nil { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err := lnwire.NewSigFromSignature(initiatorSig) + parsedSig, err := lnwire.NewSigFromSignature(bobSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } - closingSigned := lnwire.NewClosingSigned(chanID, peerFee, parsedSig) - responder.chanCloseMsgs <- &closeMsg{ + closingSigned := lnwire.NewClosingSigned(chanID, aliceFee, parsedSig) + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: closingSigned, } - // The responder will now see that we agreed on the fee, and broadcast - // the closing transaction. + // Alice should now see that we agreed on the fee, and should broadcast the + // closing transaction. select { case <-broadcastTxChan: case <-time.After(timeout): t.Fatalf("closing tx not broadcast") } - // And the initiator should be waiting for a confirmation notification. + // Need to pull the remaining message off of Alice's outgoing queue. + select { + case outMsg := <-alicePeer.outgoingQueue: + msg = outMsg.msg + case <-time.After(timeout): + t.Fatalf("did not receive ClosingSigned message") + } + if _, ok := msg.(*lnwire.ClosingSigned); !ok { + t.Fatalf("expected ClosingSigned message, got %T", msg) + } + + // Alice should be waiting in a goroutine for a confirmation. notifier.confChannel <- &chainntnfs.TxConfirmation{} } @@ -124,12 +134,12 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) { func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) { t.Parallel() - notifier := &mockNotfier{ + notifier := &mockNotifier{ confChannel: make(chan *chainntnfs.TxConfirmation), } broadcastTxChan := make(chan *wire.MsgTx) - initiator, initiatorChan, responderChan, cleanUp, err := createTestPeer( + alicePeer, bobChan, cleanUp, err := createTestPeer( notifier, broadcastTxChan, noUpdate, ) if err != nil { @@ -137,22 +147,22 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) { } defer cleanUp() - // We make the initiator send a shutdown request. + // We make Alice send a shutdown request. updateChan := make(chan interface{}, 1) errChan := make(chan error, 1) closeCommand := &htlcswitch.ChanClose{ CloseType: htlcswitch.CloseRegular, - ChanPoint: initiatorChan.ChannelPoint(), + ChanPoint: bobChan.ChannelPoint(), Updates: updateChan, TargetFeePerKw: 12500, Err: errChan, } - initiator.localCloseChanReqs <- closeCommand + alicePeer.localCloseChanReqs <- closeCommand - // We should now be getting the shutdown request. + // We can now pull a Shutdown message off of Alice's outgoingQueue. var msg lnwire.Message select { - case outMsg := <-initiator.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive shutdown request") @@ -163,68 +173,78 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) { t.Fatalf("expected Shutdown message, got %T", msg) } - initiatorDeliveryScript := shutdownMsg.Address + aliceDeliveryScript := shutdownMsg.Address - // We'll answer the shutdown message with our own Shutdown, and then a - // ClosingSigned message. + // Bob will respond with his own Shutdown message. chanID := shutdownMsg.ChannelID - initiator.chanCloseMsgs <- &closeMsg{ + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: lnwire.NewShutdown(chanID, dummyDeliveryScript), } - estimator := chainfee.NewStaticEstimator(12500, 0) - feePerKw, err := estimator.EstimateFeePerKW(1) - if err != nil { - t.Fatalf("unable to query fee estimator: %v", err) + // Alice will reply with a ClosingSigned here. + select { + case outMsg := <-alicePeer.outgoingQueue: + msg = outMsg.msg + case <-time.After(timeout): + t.Fatalf("did not receive closing signed message") } - fee := responderChan.CalcFee(feePerKw) - closeSig, _, _, err := responderChan.CreateCloseProposal(fee, - dummyDeliveryScript, initiatorDeliveryScript) + closingSignedMsg, ok := msg.(*lnwire.ClosingSigned) + if !ok { + t.Fatalf("expected to receive closing signed message, got %T", msg) + } + + // Bob should reply with the exact same fee in his next ClosingSigned + // message. + bobFee := closingSignedMsg.FeeSatoshis + bobSig, _, _, err := bobChan.CreateCloseProposal( + bobFee, dummyDeliveryScript, aliceDeliveryScript, + ) if err != nil { t.Fatalf("unable to create close proposal: %v", err) } - parsedSig, err := lnwire.NewSigFromSignature(closeSig) + parsedSig, err := lnwire.NewSigFromSignature(bobSig) if err != nil { t.Fatalf("unable to parse signature: %v", err) } closingSigned := lnwire.NewClosingSigned(shutdownMsg.ChannelID, - fee, parsedSig) - initiator.chanCloseMsgs <- &closeMsg{ + bobFee, parsedSig) + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: closingSigned, } - // And we expect the initiator to accept the fee, and broadcast the - // closing transaction. - select { - case outMsg := <-initiator.outgoingQueue: - msg = outMsg.msg - case <-time.After(timeout): - t.Fatalf("did not receive closing signed message") - } + // Alice should accept Bob's fee, broadcast the cooperative close tx, and + // send a ClosingSigned message back to Bob. - closingSignedMsg, ok := msg.(*lnwire.ClosingSigned) - if !ok { - t.Fatalf("expected ClosingSigned message, got %T", msg) - } - - if closingSignedMsg.FeeSatoshis != fee { - t.Fatalf("expected ClosingSigned fee to be %v, instead got %v", - fee, closingSignedMsg.FeeSatoshis) - } - - // The initiator will now see that we agreed on the fee, and broadcast - // the closing transaction. + // Alice should now broadcast the closing transaction. select { case <-broadcastTxChan: case <-time.After(timeout): t.Fatalf("closing tx not broadcast") } - // And the initiator should be waiting for a confirmation notification. + // Alice should respond with the ClosingSigned they both agreed upon. + select { + case outMsg := <-alicePeer.outgoingQueue: + msg = outMsg.msg + case <-time.After(timeout): + t.Fatalf("did not receive closing signed message") + } + + closingSignedMsg, ok = msg.(*lnwire.ClosingSigned) + if !ok { + t.Fatalf("expected ClosingSigned message, got %T", msg) + } + + if closingSignedMsg.FeeSatoshis != bobFee { + t.Fatalf("expected ClosingSigned fee to be %v, instead got %v", + bobFee, closingSignedMsg.FeeSatoshis) + } + + // Alice should be waiting on a single confirmation for the coop close tx. notifier.confChannel <- &chainntnfs.TxConfirmation{} } @@ -234,12 +254,12 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) { func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { t.Parallel() - notifier := &mockNotfier{ + notifier := &mockNotifier{ confChannel: make(chan *chainntnfs.TxConfirmation), } broadcastTxChan := make(chan *wire.MsgTx) - responder, responderChan, initiatorChan, cleanUp, err := createTestPeer( + alicePeer, bobChan, cleanUp, err := createTestPeer( notifier, broadcastTxChan, noUpdate, ) if err != nil { @@ -247,12 +267,12 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { } defer cleanUp() - chanID := lnwire.NewChanIDFromOutPoint(responderChan.ChannelPoint()) + chanID := lnwire.NewChanIDFromOutPoint(bobChan.ChannelPoint()) - // We send a shutdown request to Alice. She will now be the responding - // node in this shutdown procedure. We first expect Alice to answer - // this shutdown request with a Shutdown message. - responder.chanCloseMsgs <- &closeMsg{ + // Bob sends a shutdown request to Alice. She will now be the responding + // node in this shutdown procedure. We first expect Alice to answer this + // Shutdown request with a Shutdown message. + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: lnwire.NewShutdown(chanID, dummyDeliveryScript), @@ -260,7 +280,7 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { var msg lnwire.Message select { - case outMsg := <-responder.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive shutdown message") @@ -271,140 +291,152 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { t.Fatalf("expected Shutdown message, got %T", msg) } - respDeliveryScript := shutdownMsg.Address + aliceDeliveryScript := shutdownMsg.Address - // Alice will thereafter send a ClosingSigned message, indicating her - // proposed closing transaction fee. + // As Alice is the channel initiator, she will send her ClosingSigned + // message. select { - case outMsg := <-responder.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive closing signed message") } - responderClosingSigned, ok := msg.(*lnwire.ClosingSigned) + aliceClosingSigned, ok := msg.(*lnwire.ClosingSigned) if !ok { t.Fatalf("expected ClosingSigned message, got %T", msg) } - // We don't agree with the fee, and will send back one that's 2.5x. - preferredRespFee := responderClosingSigned.FeeSatoshis + // Bob doesn't agree with the fee and will send one back that's 2.5x. + preferredRespFee := aliceClosingSigned.FeeSatoshis increasedFee := btcutil.Amount(float64(preferredRespFee) * 2.5) - initiatorSig, _, _, err := initiatorChan.CreateCloseProposal( - increasedFee, dummyDeliveryScript, respDeliveryScript, + bobSig, _, _, err := bobChan.CreateCloseProposal( + increasedFee, dummyDeliveryScript, aliceDeliveryScript, ) if err != nil { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err := lnwire.NewSigFromSignature(initiatorSig) + parsedSig, err := lnwire.NewSigFromSignature(bobSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } closingSigned := lnwire.NewClosingSigned(chanID, increasedFee, parsedSig) - responder.chanCloseMsgs <- &closeMsg{ + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: closingSigned, } - // The responder will see the new fee we propose, but with current - // settings it won't accept it immediately as it differs too much by - // its ideal fee. We should get a new proposal back, which should have - // the average fee rate proposed. + // Alice will now see the new fee we propose, but with current settings it + // won't accept it immediately as it differs too much by its ideal fee. We + // should get a new proposal back, which should have the average fee rate + // proposed. select { - case outMsg := <-responder.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive closing signed message") } - responderClosingSigned, ok = msg.(*lnwire.ClosingSigned) + aliceClosingSigned, ok = msg.(*lnwire.ClosingSigned) if !ok { t.Fatalf("expected ClosingSigned message, got %T", msg) } - // The fee sent by the responder should be less than the fee we just - // sent as it should attempt to compromise. - peerFee := responderClosingSigned.FeeSatoshis - if peerFee > increasedFee { + // The fee sent by Alice should be less than the fee Bob just sent as Alice + // should attempt to compromise. + aliceFee := aliceClosingSigned.FeeSatoshis + if aliceFee > increasedFee { t.Fatalf("new fee should be less than our fee: new=%v, "+ - "prior=%v", peerFee, increasedFee) + "prior=%v", aliceFee, increasedFee) } - lastFeeResponder := peerFee + lastFeeResponder := aliceFee // We try negotiating a 2.1x fee, which should also be rejected. increasedFee = btcutil.Amount(float64(preferredRespFee) * 2.1) - initiatorSig, _, _, err = initiatorChan.CreateCloseProposal( - increasedFee, dummyDeliveryScript, respDeliveryScript, + bobSig, _, _, err = bobChan.CreateCloseProposal( + increasedFee, dummyDeliveryScript, aliceDeliveryScript, ) if err != nil { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err = lnwire.NewSigFromSignature(initiatorSig) + parsedSig, err = lnwire.NewSigFromSignature(bobSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } closingSigned = lnwire.NewClosingSigned(chanID, increasedFee, parsedSig) - responder.chanCloseMsgs <- &closeMsg{ + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: closingSigned, } - // It still won't be accepted, and we should get a new proposal, the - // average of what we proposed, and what they proposed last time. + // Bob's latest proposal still won't be accepted and Alice should send over + // a new ClosingSigned message. It should be the average of what Bob and + // Alice each proposed last time. select { - case outMsg := <-responder.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive closing signed message") } - responderClosingSigned, ok = msg.(*lnwire.ClosingSigned) + aliceClosingSigned, ok = msg.(*lnwire.ClosingSigned) if !ok { t.Fatalf("expected ClosingSigned message, got %T", msg) } - // The peer should inch towards our fee, in order to compromise. - // Additionally, this fee should be less than the fee we sent prior. - peerFee = responderClosingSigned.FeeSatoshis - if peerFee < lastFeeResponder { + // Alice should inch towards Bob's fee, in order to compromise. + // Additionally, this fee should be less than the fee Bob sent before. + aliceFee = aliceClosingSigned.FeeSatoshis + if aliceFee < lastFeeResponder { t.Fatalf("new fee should be greater than prior: new=%v, "+ - "prior=%v", peerFee, lastFeeResponder) + "prior=%v", aliceFee, lastFeeResponder) } - if peerFee > increasedFee { - t.Fatalf("new fee should be less than our fee: new=%v, "+ - "prior=%v", peerFee, increasedFee) + if aliceFee > increasedFee { + t.Fatalf("new fee should be less than Bob's fee: new=%v, "+ + "prior=%v", aliceFee, increasedFee) } - // Finally, we'll accept the fee by echoing back the same fee that they - // sent to us. - initiatorSig, _, _, err = initiatorChan.CreateCloseProposal( - peerFee, dummyDeliveryScript, respDeliveryScript, + // Finally, Bob will accept the fee by echoing back the same fee that Alice + // just sent over. + bobSig, _, _, err = bobChan.CreateCloseProposal( + aliceFee, dummyDeliveryScript, aliceDeliveryScript, ) if err != nil { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err = lnwire.NewSigFromSignature(initiatorSig) + parsedSig, err = lnwire.NewSigFromSignature(bobSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } - closingSigned = lnwire.NewClosingSigned(chanID, peerFee, parsedSig) - responder.chanCloseMsgs <- &closeMsg{ + closingSigned = lnwire.NewClosingSigned(chanID, aliceFee, parsedSig) + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: closingSigned, } - // The responder will now see that we agreed on the fee, and broadcast - // the closing transaction. + // Alice will now see that Bob agreed on the fee, and broadcast the coop + // close transaction. select { case <-broadcastTxChan: case <-time.After(timeout): t.Fatalf("closing tx not broadcast") } - // And the responder should be waiting for a confirmation notification. + // Alice should respond with the ClosingSigned they both agreed upon. + select { + case outMsg := <-alicePeer.outgoingQueue: + msg = outMsg.msg + case <-time.After(timeout): + t.Fatalf("did not receive closing signed message") + } + if _, ok := msg.(*lnwire.ClosingSigned); !ok { + t.Fatalf("expected to receive closing signed message, got %T", msg) + } + + // Alice should be waiting on a single confirmation for the coop close tx. notifier.confChannel <- &chainntnfs.TxConfirmation{} } @@ -414,12 +446,12 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { t.Parallel() - notifier := &mockNotfier{ + notifier := &mockNotifier{ confChannel: make(chan *chainntnfs.TxConfirmation), } broadcastTxChan := make(chan *wire.MsgTx) - initiator, initiatorChan, responderChan, cleanUp, err := createTestPeer( + alicePeer, bobChan, cleanUp, err := createTestPeer( notifier, broadcastTxChan, noUpdate, ) if err != nil { @@ -432,18 +464,18 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { errChan := make(chan error, 1) closeCommand := &htlcswitch.ChanClose{ CloseType: htlcswitch.CloseRegular, - ChanPoint: initiatorChan.ChannelPoint(), + ChanPoint: bobChan.ChannelPoint(), Updates: updateChan, TargetFeePerKw: 12500, Err: errChan, } - initiator.localCloseChanReqs <- closeCommand + alicePeer.localCloseChanReqs <- closeCommand - // We should now be getting the shutdown request. + // Alice should now send a Shutdown request to Bob. var msg lnwire.Message select { - case outMsg := <-initiator.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive shutdown request") @@ -454,47 +486,20 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { t.Fatalf("expected Shutdown message, got %T", msg) } - initiatorDeliveryScript := shutdownMsg.Address + aliceDeliveryScript := shutdownMsg.Address - // We'll answer the shutdown message with our own Shutdown, and then a - // ClosingSigned message. - chanID := lnwire.NewChanIDFromOutPoint(initiatorChan.ChannelPoint()) + // Bob will answer the Shutdown message with his own Shutdown. + chanID := lnwire.NewChanIDFromOutPoint(bobChan.ChannelPoint()) respShutdown := lnwire.NewShutdown(chanID, dummyDeliveryScript) - initiator.chanCloseMsgs <- &closeMsg{ + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: respShutdown, } - estimator := chainfee.NewStaticEstimator(12500, 0) - initiatorIdealFeeRate, err := estimator.EstimateFeePerKW(1) - if err != nil { - t.Fatalf("unable to query fee estimator: %v", err) - } - initiatorIdealFee := responderChan.CalcFee(initiatorIdealFeeRate) - increasedFee := btcutil.Amount(float64(initiatorIdealFee) * 2.5) - closeSig, _, _, err := responderChan.CreateCloseProposal( - increasedFee, dummyDeliveryScript, initiatorDeliveryScript, - ) - if err != nil { - t.Fatalf("unable to create close proposal: %v", err) - } - parsedSig, err := lnwire.NewSigFromSignature(closeSig) - if err != nil { - t.Fatalf("unable to parse signature: %v", err) - } - - closingSigned := lnwire.NewClosingSigned( - shutdownMsg.ChannelID, increasedFee, parsedSig, - ) - initiator.chanCloseMsgs <- &closeMsg{ - cid: chanID, - msg: closingSigned, - } - - // We should get two closing signed messages, the first will be the - // ideal fee sent by the initiator in response to our shutdown request. + // Alice should now respond with a ClosingSigned message with her ideal + // fee rate. select { - case outMsg := <-initiator.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive closing signed") @@ -503,16 +508,35 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { if !ok { t.Fatalf("expected ClosingSigned message, got %T", msg) } - if closingSignedMsg.FeeSatoshis != initiatorIdealFee { - t.Fatalf("expected ClosingSigned fee to be %v, instead got %v", - initiatorIdealFee, closingSignedMsg.FeeSatoshis) - } - lastFeeSent := closingSignedMsg.FeeSatoshis - // The second message should be the compromise fee sent in response to - // them receiving our fee proposal. + idealFeeRate := closingSignedMsg.FeeSatoshis + lastReceivedFee := idealFeeRate + + increasedFee := btcutil.Amount(float64(idealFeeRate) * 2.1) + lastSentFee := increasedFee + + bobSig, _, _, err := bobChan.CreateCloseProposal( + increasedFee, dummyDeliveryScript, aliceDeliveryScript, + ) + if err != nil { + t.Fatalf("error creating close proposal: %v", err) + } + + parsedSig, err := lnwire.NewSigFromSignature(bobSig) + if err != nil { + t.Fatalf("unable to parse signature: %v", err) + } + + closingSigned := lnwire.NewClosingSigned(chanID, increasedFee, parsedSig) + alicePeer.chanCloseMsgs <- &closeMsg{ + cid: chanID, + msg: closingSigned, + } + + // It still won't be accepted, and we should get a new proposal, the + // average of what we proposed, and what they proposed last time. select { - case outMsg := <-initiator.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive closing signed") @@ -522,80 +546,79 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { t.Fatalf("expected ClosingSigned message, got %T", msg) } - // The peer should inch towards our fee, in order to compromise. - // Additionally, this fee should be less than the fee we sent prior. - peerFee := closingSignedMsg.FeeSatoshis - if peerFee < lastFeeSent { - t.Fatalf("new fee should be greater than prior: new=%v, "+ - "prior=%v", peerFee, lastFeeSent) + aliceFee := closingSignedMsg.FeeSatoshis + if aliceFee < lastReceivedFee { + t.Fatalf("new fee should be greater than prior: new=%v, old=%v", + aliceFee, lastReceivedFee) } - if peerFee > increasedFee { - t.Fatalf("new fee should be less than our fee: new=%v, "+ - "prior=%v", peerFee, increasedFee) + if aliceFee > lastSentFee { + t.Fatalf("new fee should be less than our fee: new=%v, old=%v", + aliceFee, lastSentFee) } - lastFeeSent = closingSignedMsg.FeeSatoshis - // We try negotiating a 2.1x fee, which should also be rejected. - increasedFee = btcutil.Amount(float64(initiatorIdealFee) * 2.1) - responderSig, _, _, err := responderChan.CreateCloseProposal( - increasedFee, dummyDeliveryScript, initiatorDeliveryScript, + lastReceivedFee = aliceFee + + // We'll try negotiating a 1.5x fee, which should also be rejected. + increasedFee = btcutil.Amount(float64(idealFeeRate) * 1.5) + lastSentFee = increasedFee + + bobSig, _, _, err = bobChan.CreateCloseProposal( + increasedFee, dummyDeliveryScript, aliceDeliveryScript, ) if err != nil { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err = lnwire.NewSigFromSignature(responderSig) + parsedSig, err = lnwire.NewSigFromSignature(bobSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } closingSigned = lnwire.NewClosingSigned(chanID, increasedFee, parsedSig) - initiator.chanCloseMsgs <- &closeMsg{ + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: closingSigned, } - // It still won't be accepted, and we should get a new proposal, the - // average of what we proposed, and what they proposed last time. + // Alice won't accept Bob's new proposal, and Bob should receive a new + // proposal which is the average of what Bob proposed and Alice proposed + // last time. select { - case outMsg := <-initiator.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive closing signed") } - - initiatorClosingSigned, ok := msg.(*lnwire.ClosingSigned) + closingSignedMsg, ok = msg.(*lnwire.ClosingSigned) if !ok { t.Fatalf("expected ClosingSigned message, got %T", msg) } - // Once again, the fee sent by the initiator should be greater than the - // last fee they sent, but less than the last fee we sent. - peerFee = initiatorClosingSigned.FeeSatoshis - if peerFee < lastFeeSent { - t.Fatalf("new fee should be greater than prior: new=%v, "+ - "prior=%v", peerFee, lastFeeSent) + aliceFee = closingSignedMsg.FeeSatoshis + if aliceFee < lastReceivedFee { + t.Fatalf("new fee should be greater than prior: new=%v, old=%v", + aliceFee, lastReceivedFee) } - if peerFee > increasedFee { - t.Fatalf("new fee should be less than our fee: new=%v, "+ - "prior=%v", peerFee, increasedFee) + if aliceFee > lastSentFee { + t.Fatalf("new fee should be less than Bob's fee: new=%v, old=%v", + aliceFee, lastSentFee) } - // At this point, we'll accept their fee by sending back a CloseSigned - // message with an identical fee. - responderSig, _, _, err = responderChan.CreateCloseProposal( - peerFee, dummyDeliveryScript, initiatorDeliveryScript, + // Bob will now accept their fee by sending back a ClosingSigned message + // with an identical fee. + bobSig, _, _, err = bobChan.CreateCloseProposal( + aliceFee, dummyDeliveryScript, aliceDeliveryScript, ) if err != nil { t.Fatalf("error creating close proposal: %v", err) } - parsedSig, err = lnwire.NewSigFromSignature(responderSig) + parsedSig, err = lnwire.NewSigFromSignature(bobSig) if err != nil { t.Fatalf("error parsing signature: %v", err) } - closingSigned = lnwire.NewClosingSigned(chanID, peerFee, parsedSig) - initiator.chanCloseMsgs <- &closeMsg{ + closingSigned = lnwire.NewClosingSigned(chanID, aliceFee, parsedSig) + alicePeer.chanCloseMsgs <- &closeMsg{ cid: chanID, msg: closingSigned, } @@ -606,6 +629,20 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { case <-time.After(timeout): t.Fatalf("closing tx not broadcast") } + + // Alice should respond with the ClosingSigned they both agreed upon. + select { + case outMsg := <-alicePeer.outgoingQueue: + msg = outMsg.msg + case <-time.After(timeout): + t.Fatalf("did not receive closing signed message") + } + if _, ok := msg.(*lnwire.ClosingSigned); !ok { + t.Fatalf("expected to receive closing signed message, got %T", msg) + } + + // Alice should be waiting on a single confirmation for the coop close tx. + notifier.confChannel <- &chainntnfs.TxConfirmation{} } // TestChooseDeliveryScript tests that chooseDeliveryScript correctly errors @@ -742,13 +779,13 @@ func TestCustomShutdownScript(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { - notifier := &mockNotfier{ + notifier := &mockNotifier{ confChannel: make(chan *chainntnfs.TxConfirmation), } broadcastTxChan := make(chan *wire.MsgTx) // Open a channel. - initiator, initiatorChan, _, cleanUp, err := createTestPeer( + alicePeer, bobChan, cleanUp, err := createTestPeer( notifier, broadcastTxChan, test.update, ) if err != nil { @@ -760,7 +797,7 @@ func TestCustomShutdownScript(t *testing.T) { // a specified delivery address. updateChan := make(chan interface{}, 1) errChan := make(chan error, 1) - chanPoint := initiatorChan.ChannelPoint() + chanPoint := bobChan.ChannelPoint() closeCommand := htlcswitch.ChanClose{ CloseType: htlcswitch.CloseRegular, ChanPoint: chanPoint, @@ -772,11 +809,11 @@ func TestCustomShutdownScript(t *testing.T) { // Send the close command for the correct channel and check that a // shutdown message is sent. - initiator.localCloseChanReqs <- &closeCommand + alicePeer.localCloseChanReqs <- &closeCommand var msg lnwire.Message select { - case outMsg := <-initiator.outgoingQueue: + case outMsg := <-alicePeer.outgoingQueue: msg = outMsg.msg case <-time.After(timeout): t.Fatalf("did not receive shutdown message") @@ -820,7 +857,7 @@ func genScript(t *testing.T, address string) lnwire.DeliveryAddress { // Generate an address which can be used for testing. deliveryAddr, err := btcutil.DecodeAddress( address, - activeNetParams.Params, + &chaincfg.TestNet3Params, ) if err != nil { t.Fatalf("invalid delivery address: %v", err) diff --git a/peer/config.go b/peer/config.go new file mode 100644 index 000000000..8c0f93139 --- /dev/null +++ b/peer/config.go @@ -0,0 +1,228 @@ +package peer + +import ( + "net" + "time" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/connmgr" + + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/contractcourt" + "github.com/lightningnetwork/lnd/discovery" + "github.com/lightningnetwork/lnd/htlcswitch" + "github.com/lightningnetwork/lnd/htlcswitch/hodl" + "github.com/lightningnetwork/lnd/htlcswitch/hop" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/invoices" + "github.com/lightningnetwork/lnd/lnpeer" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/netann" + "github.com/lightningnetwork/lnd/pool" + "github.com/lightningnetwork/lnd/queue" + "github.com/lightningnetwork/lnd/watchtower/wtclient" +) + +// Config defines configuration fields that are necessary for a peer object +// to function. +type Config struct { + // Conn is the underlying network connection for this peer. + Conn net.Conn + + // ConnReq stores information related to the persistent connection request + // for this peer. + ConnReq *connmgr.ConnReq + + // PubKeyBytes is the serialized, compressed public key of this peer. + PubKeyBytes [33]byte + + // Addr is the network address of the peer. + Addr *lnwire.NetAddress + + // Inbound indicates whether or not the peer is an inbound peer. + Inbound bool + + // Features is the set of features that we advertise to the remote party. + Features *lnwire.FeatureVector + + // LegacyFeatures is the set of features that we advertise to the remote + // peer for backwards compatibility. Nodes that have not implemented + // flat features will still be able to read our feature bits from the + // legacy global field, but we will also advertise everything in the + // default features field. + LegacyFeatures *lnwire.FeatureVector + + // OutgoingCltvRejectDelta defines the number of blocks before expiry of + // an htlc where we don't offer it anymore. + OutgoingCltvRejectDelta uint32 + + // ChanActiveTimeout specifies the duration the peer will wait to request + // a channel reenable, beginning from the time the peer was started. + ChanActiveTimeout time.Duration + + // ErrorBuffer stores a set of errors related to a peer. It contains error + // messages that our peer has recently sent us over the wire and records of + // unknown messages that were sent to us so that we can have a full track + // record of the communication errors we have had with our peer. If we + // choose to disconnect from a peer, it also stores the reason we had for + // disconnecting. + ErrorBuffer *queue.CircularBuffer + + // WritePool is the task pool that manages reuse of write buffers. Write + // tasks are submitted to the pool in order to conserve the total number of + // write buffers allocated at any one time, and decouple write buffer + // allocation from the peer life cycle. + WritePool *pool.Write + + // ReadPool is the task pool that manages reuse of read buffers. + ReadPool *pool.Read + + // Switch is a pointer to the htlcswitch. It is used to setup, get, and + // tear-down ChannelLinks. + Switch *htlcswitch.Switch + + // InterceptSwitch is a pointer to the InterceptableSwitch, a wrapper around + // the regular Switch. We only export it here to pass ForwardPackets to the + // ChannelLinkConfig. + InterceptSwitch *htlcswitch.InterceptableSwitch + + // ChannelDB is used to fetch opened channels, closed channels, and the + // channel graph. + ChannelDB *channeldb.DB + + // ChainArb is used to subscribe to channel events, update contract signals, + // and force close channels. + ChainArb *contractcourt.ChainArbitrator + + // AuthGossiper is needed so that the Brontide impl can register with the + // gossiper and process remote channel announcements. + AuthGossiper *discovery.AuthenticatedGossiper + + // ChanStatusMgr is used to set or un-set the disabled bit in channel + // updates. + ChanStatusMgr *netann.ChanStatusManager + + // ChainIO is used to retrieve the best block. + ChainIO lnwallet.BlockChainIO + + // FeeEstimator is used to compute our target ideal fee-per-kw when + // initializing the coop close process. + FeeEstimator chainfee.Estimator + + // Signer is used when creating *lnwallet.LightningChannel instances. + Signer input.Signer + + // SigPool is used when creating *lnwallet.LightningChannel instances. + SigPool *lnwallet.SigPool + + // Wallet is used to publish transactions and generate delivery scripts + // during the coop close process. + Wallet *lnwallet.LightningWallet + + // ChainNotifier is used to receive confirmations of a coop close + // transaction. + ChainNotifier chainntnfs.ChainNotifier + + // RoutingPolicy is used to set the forwarding policy for links created by + // the Brontide. + RoutingPolicy htlcswitch.ForwardingPolicy + + // Sphinx is used when setting up ChannelLinks so they can decode sphinx + // onion blobs. + Sphinx *hop.OnionProcessor + + // WitnessBeacon is used when setting up ChannelLinks so they can add any + // preimages that they learn. + WitnessBeacon contractcourt.WitnessBeacon + + // Invoices is passed to the ChannelLink on creation and handles all + // invoice-related logic. + Invoices *invoices.InvoiceRegistry + + // ChannelNotifier is used by the link to notify other sub-systems about + // channel-related events and by the Brontide to subscribe to + // ActiveLinkEvents. + ChannelNotifier *channelnotifier.ChannelNotifier + + // HtlcNotifier is used when creating a ChannelLink. + HtlcNotifier *htlcswitch.HtlcNotifier + + // TowerClient is used when creating a ChannelLink. + TowerClient wtclient.Client + + // DisconnectPeer is used to disconnect this peer if the cooperative close + // process fails. + DisconnectPeer func(*btcec.PublicKey) error + + // GenNodeAnnouncement is used to send our node announcement to the remote + // on startup. + GenNodeAnnouncement func(bool, + ...netann.NodeAnnModifier) (lnwire.NodeAnnouncement, error) + + // PrunePersistentPeerConnection is used to remove all internal state + // related to this peer in the server. + PrunePersistentPeerConnection func([33]byte) + + // FetchLastChanUpdate fetches our latest channel update for a target + // channel. + FetchLastChanUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate, + error) + + // ProcessFundingOpen is used to hand off an OpenChannel message to the + // funding manager. + ProcessFundingOpen func(*lnwire.OpenChannel, lnpeer.Peer) + + // ProcessFundingAccept is used to hand off an AcceptChannel message to the + // funding manager. + ProcessFundingAccept func(*lnwire.AcceptChannel, lnpeer.Peer) + + // ProcessFundingCreated is used to hand off a FundingCreated message to + // the funding manager. + ProcessFundingCreated func(*lnwire.FundingCreated, lnpeer.Peer) + + // ProcessFundingSigned is used to hand off a FundingSigned message to the + // funding manager. + ProcessFundingSigned func(*lnwire.FundingSigned, lnpeer.Peer) + + // ProcessFundingLocked is used to hand off a FundingLocked message to the + // funding manager. + ProcessFundingLocked func(*lnwire.FundingLocked, lnpeer.Peer) + + // ProcessFundingError is used to hand off an Error message to the funding + // manager. + ProcessFundingError func(*lnwire.Error, *btcec.PublicKey) + + // IsPendingChannel is used to determine whether to send an Error message + // to the funding manager or not. + IsPendingChannel func([32]byte, *btcec.PublicKey) bool + + // Hodl is used when creating ChannelLinks to specify HodlFlags as + // breakpoints in dev builds. + Hodl *hodl.Config + + // UnsafeReplay is used when creating ChannelLinks to specify whether or + // not to replay adds on its commitment tx. + UnsafeReplay bool + + // MaxOutgoingCltvExpiry is used when creating ChannelLinks and is the max + // number of blocks that funds could be locked up for when forwarding + // payments. + MaxOutgoingCltvExpiry uint32 + + // MaxChannelFeeAllocation is used when creating ChannelLinks and is the + // maximum percentage of total funds that can be allocated to a channel's + // commitment fee. This only applies for the initiator of the channel. + MaxChannelFeeAllocation float64 + + // ServerPubKey is the serialized, compressed public key of our lnd node. + // It is used to determine which policy (channel edge) to pass to the + // ChannelLink. + ServerPubKey [33]byte + + // Quit is the server's quit channel. If this is closed, we halt operation. + Quit chan struct{} +} diff --git a/peer/interfaces.go b/peer/interfaces.go new file mode 100644 index 000000000..05fe4a983 --- /dev/null +++ b/peer/interfaces.go @@ -0,0 +1,11 @@ +package peer + +import "github.com/lightningnetwork/lnd/lnwire" + +// LinkUpdater is an interface implemented by most messages in BOLT 2 that are +// allowed to update the channel state. +type LinkUpdater interface { + // TargetChanID returns the channel id of the link for which this message + // is intended. + TargetChanID() lnwire.ChannelID +} diff --git a/peer/log.go b/peer/log.go new file mode 100644 index 000000000..a1f9bda3e --- /dev/null +++ b/peer/log.go @@ -0,0 +1,40 @@ +package peer + +import ( + "github.com/btcsuite/btclog" + "github.com/lightningnetwork/lnd/build" +) + +// peerLog is a logger that is initialized with the btclog.Disabled logger. +var peerLog btclog.Logger + +// The default amount of logging is none. +func init() { + UseLogger(build.NewSubLogger("PEER", nil)) +} + +// DisableLog disables all logging output. +func DisableLog() { + UseLogger(btclog.Disabled) +} + +// UseLogger uses a specified Logger to output package logging info. +func UseLogger(logger btclog.Logger) { + peerLog = logger +} + +// logClosure is used to provide a closure over expensive logging operations +// so they aren't performed when the logging level doesn't warrant it. +type logClosure func() string + +// String invokes the underlying function and returns the result. +func (c logClosure) String() string { + return c() +} + +// newLogClosure returns a new closure over a function that returns a string +// which itself provides a Stringer interface so that it can be used with the +// logging system. +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} diff --git a/peer/test_utils.go b/peer/test_utils.go new file mode 100644 index 000000000..df74eda11 --- /dev/null +++ b/peer/test_utils.go @@ -0,0 +1,695 @@ +package peer + +import ( + "bytes" + crand "crypto/rand" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + "math/rand" + "net" + "os" + "time" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/wallet/txauthor" + "github.com/btcsuite/btcwallet/wtxmgr" + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/htlcswitch" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/netann" + "github.com/lightningnetwork/lnd/queue" + "github.com/lightningnetwork/lnd/shachain" + "github.com/lightningnetwork/lnd/ticker" +) + +const ( + broadcastHeight = 100 +) + +var ( + alicesPrivKey = []byte{ + 0x2b, 0xd8, 0x06, 0xc9, 0x7f, 0x0e, 0x00, 0xaf, + 0x1a, 0x1f, 0xc3, 0x32, 0x8f, 0xa7, 0x63, 0xa9, + 0x26, 0x97, 0x23, 0xc8, 0xdb, 0x8f, 0xac, 0x4f, + 0x93, 0xaf, 0x71, 0xdb, 0x18, 0x6d, 0x6e, 0x90, + } + + bobsPrivKey = []byte{ + 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, + 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, + 0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, + 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, + } + + // Use a hard-coded HD seed. + testHdSeed = [32]byte{ + 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, + 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, + 0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9, + 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, + } + + // Just use some arbitrary bytes as delivery script. + dummyDeliveryScript = alicesPrivKey + + // testTx is used as the default funding txn for single-funder channels. + testTx = &wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: chainhash.Hash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 5000000000, + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 5, + } +) + +// noUpdate is a function which can be used as a parameter in createTestPeer to +// call the setup code with no custom values on the channels set up. +var noUpdate = func(a, b *channeldb.OpenChannel) {} + +type mockSigner struct { + key *btcec.PrivateKey +} + +func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, + signDesc *input.SignDescriptor) (input.Signature, error) { + amt := signDesc.Output.Value + witnessScript := signDesc.WitnessScript + privKey := m.key + + if !privKey.PubKey().IsEqual(signDesc.KeyDesc.PubKey) { + return nil, fmt.Errorf("incorrect key passed") + } + + switch { + case signDesc.SingleTweak != nil: + privKey = input.TweakPrivKey(privKey, + signDesc.SingleTweak) + case signDesc.DoubleTweak != nil: + privKey = input.DeriveRevocationPrivKey(privKey, + signDesc.DoubleTweak) + } + + sig, err := txscript.RawTxInWitnessSignature(tx, signDesc.SigHashes, + signDesc.InputIndex, amt, witnessScript, signDesc.HashType, + privKey) + if err != nil { + return nil, err + } + + return btcec.ParseDERSignature(sig[:len(sig)-1], btcec.S256()) +} + +func (m *mockSigner) ComputeInputScript(tx *wire.MsgTx, + signDesc *input.SignDescriptor) (*input.Script, error) { + + // TODO(roasbeef): expose tweaked signer from lnwallet so don't need to + // duplicate this code? + + privKey := m.key + + switch { + case signDesc.SingleTweak != nil: + privKey = input.TweakPrivKey(privKey, + signDesc.SingleTweak) + case signDesc.DoubleTweak != nil: + privKey = input.DeriveRevocationPrivKey(privKey, + signDesc.DoubleTweak) + } + + witnessScript, err := txscript.WitnessSignature(tx, signDesc.SigHashes, + signDesc.InputIndex, signDesc.Output.Value, signDesc.Output.PkScript, + signDesc.HashType, privKey, true) + if err != nil { + return nil, err + } + + return &input.Script{ + Witness: witnessScript, + }, nil +} + +var _ input.Signer = (*mockSigner)(nil) + +type mockChainIO struct { + bestHeight int32 +} + +func (m *mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) { + return nil, m.bestHeight, nil +} + +func (*mockChainIO) GetUtxo(op *wire.OutPoint, _ []byte, + heightHint uint32, _ <-chan struct{}) (*wire.TxOut, error) { + return nil, nil +} + +func (*mockChainIO) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) { + return nil, nil +} + +func (*mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) { + return nil, nil +} + +var _ lnwallet.BlockChainIO = (*mockChainIO)(nil) + +type mockWalletController struct { + rootKey *btcec.PrivateKey + publishedTxns chan *wire.MsgTx +} + +func (*mockWalletController) FetchInputInfo(prevOut *wire.OutPoint) ( + *lnwallet.Utxo, error) { + + return nil, nil +} + +func (*mockWalletController) ConfirmedBalance(confs int32) (btcutil.Amount, + error) { + + return 0, nil +} + +func (m *mockWalletController) NewAddress(addrType lnwallet.AddressType, + change bool) (btcutil.Address, error) { + + addr, _ := btcutil.NewAddressPubKey( + m.rootKey.PubKey().SerializeCompressed(), &chaincfg.MainNetParams, + ) + return addr, nil +} + +func (*mockWalletController) LastUnusedAddress(addrType lnwallet.AddressType) ( + btcutil.Address, error) { + + return nil, nil +} + +func (*mockWalletController) IsOurAddress(a btcutil.Address) bool { + return false +} + +func (*mockWalletController) SendOutputs(outputs []*wire.TxOut, + feeRate chainfee.SatPerKWeight, label string) (*wire.MsgTx, error) { + + return nil, nil +} + +func (*mockWalletController) CreateSimpleTx(outputs []*wire.TxOut, + feeRate chainfee.SatPerKWeight, dryRun bool) (*txauthor.AuthoredTx, error) { + + return nil, nil +} + +func (*mockWalletController) ListUnspentWitness(minconfirms, + maxconfirms int32) ([]*lnwallet.Utxo, error) { + + return nil, nil +} + +func (*mockWalletController) ListTransactionDetails(startHeight, + endHeight int32) ([]*lnwallet.TransactionDetail, error) { + + return nil, nil +} + +func (*mockWalletController) LockOutpoint(o wire.OutPoint) {} + +func (*mockWalletController) UnlockOutpoint(o wire.OutPoint) {} + +func (m *mockWalletController) PublishTransaction(tx *wire.MsgTx, + label string) error { + m.publishedTxns <- tx + return nil +} + +func (*mockWalletController) LabelTransaction(hash chainhash.Hash, + label string, overwrite bool) error { + + return nil +} + +func (*mockWalletController) SubscribeTransactions() ( + lnwallet.TransactionSubscription, error) { + + return nil, nil +} + +func (*mockWalletController) IsSynced() (bool, int64, error) { + return false, 0, nil +} + +func (*mockWalletController) Start() error { + return nil +} + +func (*mockWalletController) Stop() error { + return nil +} + +func (*mockWalletController) BackEnd() string { + return "" +} + +func (*mockWalletController) LeaseOutput(wtxmgr.LockID, + wire.OutPoint) (time.Time, error) { + + return time.Now(), nil +} + +func (*mockWalletController) ReleaseOutput(wtxmgr.LockID, wire.OutPoint) error { + return nil +} + +func (*mockWalletController) GetRecoveryInfo() (bool, float64, error) { + return false, 0, nil +} + +var _ lnwallet.WalletController = (*mockWalletController)(nil) + +type mockNotifier struct { + confChannel chan *chainntnfs.TxConfirmation +} + +func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, + _ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, + error) { + + return &chainntnfs.ConfirmationEvent{ + Confirmed: m.confChannel, + }, nil +} + +func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte, + heightHint uint32) (*chainntnfs.SpendEvent, error) { + + return &chainntnfs.SpendEvent{ + Spend: make(chan *chainntnfs.SpendDetail), + Cancel: func() {}, + }, nil +} + +func (m *mockNotifier) RegisterBlockEpochNtfn( + bestBlock *chainntnfs.BlockEpoch) (*chainntnfs.BlockEpochEvent, error) { + + return &chainntnfs.BlockEpochEvent{ + Epochs: make(chan *chainntnfs.BlockEpoch), + Cancel: func() {}, + }, nil +} + +func (m *mockNotifier) Start() error { + return nil +} + +func (m *mockNotifier) Stop() error { + return nil +} + +func (m *mockNotifier) Started() bool { + return true +} + +var _ chainntnfs.ChainNotifier = (*mockNotifier)(nil) + +// createTestPeer creates a channel between two nodes, and returns a peer for +// one of the nodes, together with the channel seen from both nodes. It takes +// an updateChan function which can be used to modify the default values on +// the channel states for each peer. +func createTestPeer(notifier chainntnfs.ChainNotifier, + publTx chan *wire.MsgTx, updateChan func(a, b *channeldb.OpenChannel)) ( + *Brontide, *lnwallet.LightningChannel, func(), error) { + + aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes( + btcec.S256(), alicesPrivKey, + ) + aliceKeySigner := &keychain.PrivKeyDigestSigner{PrivKey: aliceKeyPriv} + bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes( + btcec.S256(), bobsPrivKey, + ) + + channelCapacity := btcutil.Amount(10 * 1e8) + channelBal := channelCapacity / 2 + aliceDustLimit := btcutil.Amount(200) + bobDustLimit := btcutil.Amount(1300) + csvTimeoutAlice := uint32(5) + csvTimeoutBob := uint32(4) + + prevOut := &wire.OutPoint{ + Hash: chainhash.Hash(testHdSeed), + Index: 0, + } + fundingTxIn := wire.NewTxIn(prevOut, nil, nil) + + aliceCfg := channeldb.ChannelConfig{ + ChannelConstraints: channeldb.ChannelConstraints{ + DustLimit: aliceDustLimit, + MaxPendingAmount: lnwire.MilliSatoshi(rand.Int63()), + ChanReserve: btcutil.Amount(rand.Int63()), + MinHTLC: lnwire.MilliSatoshi(rand.Int63()), + MaxAcceptedHtlcs: uint16(rand.Int31()), + CsvDelay: uint16(csvTimeoutAlice), + }, + MultiSigKey: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + } + bobCfg := channeldb.ChannelConfig{ + ChannelConstraints: channeldb.ChannelConstraints{ + DustLimit: bobDustLimit, + MaxPendingAmount: lnwire.MilliSatoshi(rand.Int63()), + ChanReserve: btcutil.Amount(rand.Int63()), + MinHTLC: lnwire.MilliSatoshi(rand.Int63()), + MaxAcceptedHtlcs: uint16(rand.Int31()), + CsvDelay: uint16(csvTimeoutBob), + }, + MultiSigKey: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + } + + bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize()) + if err != nil { + return nil, nil, nil, err + } + bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) + bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) + if err != nil { + return nil, nil, nil, err + } + bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:]) + + aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize()) + if err != nil { + return nil, nil, nil, err + } + alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) + aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) + if err != nil { + return nil, nil, nil, err + } + aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) + + aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns( + channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, + bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit, + ) + if err != nil { + return nil, nil, nil, err + } + + alicePath, err := ioutil.TempDir("", "alicedb") + if err != nil { + return nil, nil, nil, err + } + + dbAlice, err := channeldb.Open(alicePath) + if err != nil { + return nil, nil, nil, err + } + + bobPath, err := ioutil.TempDir("", "bobdb") + if err != nil { + return nil, nil, nil, err + } + + dbBob, err := channeldb.Open(bobPath) + if err != nil { + return nil, nil, nil, err + } + + estimator := chainfee.NewStaticEstimator(12500, 0) + feePerKw, err := estimator.EstimateFeePerKW(1) + if err != nil { + return nil, nil, nil, err + } + + // TODO(roasbeef): need to factor in commit fee? + aliceCommit := channeldb.ChannelCommitment{ + CommitHeight: 0, + LocalBalance: lnwire.NewMSatFromSatoshis(channelBal), + RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal), + FeePerKw: btcutil.Amount(feePerKw), + CommitFee: feePerKw.FeeForWeight(input.CommitWeight), + CommitTx: aliceCommitTx, + CommitSig: bytes.Repeat([]byte{1}, 71), + } + bobCommit := channeldb.ChannelCommitment{ + CommitHeight: 0, + LocalBalance: lnwire.NewMSatFromSatoshis(channelBal), + RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal), + FeePerKw: btcutil.Amount(feePerKw), + CommitFee: feePerKw.FeeForWeight(input.CommitWeight), + CommitTx: bobCommitTx, + CommitSig: bytes.Repeat([]byte{1}, 71), + } + + var chanIDBytes [8]byte + if _, err := io.ReadFull(crand.Reader, chanIDBytes[:]); err != nil { + return nil, nil, nil, err + } + + shortChanID := lnwire.NewShortChanIDFromInt( + binary.BigEndian.Uint64(chanIDBytes[:]), + ) + + aliceChannelState := &channeldb.OpenChannel{ + LocalChanCfg: aliceCfg, + RemoteChanCfg: bobCfg, + IdentityPub: aliceKeyPub, + FundingOutpoint: *prevOut, + ShortChannelID: shortChanID, + ChanType: channeldb.SingleFunderTweaklessBit, + IsInitiator: true, + Capacity: channelCapacity, + RemoteCurrentRevocation: bobCommitPoint, + RevocationProducer: alicePreimageProducer, + RevocationStore: shachain.NewRevocationStore(), + LocalCommitment: aliceCommit, + RemoteCommitment: aliceCommit, + Db: dbAlice, + Packager: channeldb.NewChannelPackager(shortChanID), + FundingTxn: testTx, + } + bobChannelState := &channeldb.OpenChannel{ + LocalChanCfg: bobCfg, + RemoteChanCfg: aliceCfg, + IdentityPub: bobKeyPub, + FundingOutpoint: *prevOut, + ChanType: channeldb.SingleFunderTweaklessBit, + IsInitiator: false, + Capacity: channelCapacity, + RemoteCurrentRevocation: aliceCommitPoint, + RevocationProducer: bobPreimageProducer, + RevocationStore: shachain.NewRevocationStore(), + LocalCommitment: bobCommit, + RemoteCommitment: bobCommit, + Db: dbBob, + Packager: channeldb.NewChannelPackager(shortChanID), + } + + // Set custom values on the channel states. + updateChan(aliceChannelState, bobChannelState) + + aliceAddr := &net.TCPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: 18555, + } + + if err := aliceChannelState.SyncPending(aliceAddr, 0); err != nil { + return nil, nil, nil, err + } + + bobAddr := &net.TCPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: 18556, + } + + if err := bobChannelState.SyncPending(bobAddr, 0); err != nil { + return nil, nil, nil, err + } + + cleanUpFunc := func() { + os.RemoveAll(bobPath) + os.RemoveAll(alicePath) + } + + aliceSigner := &mockSigner{aliceKeyPriv} + bobSigner := &mockSigner{bobKeyPriv} + + alicePool := lnwallet.NewSigPool(1, aliceSigner) + channelAlice, err := lnwallet.NewLightningChannel( + aliceSigner, aliceChannelState, alicePool, + ) + if err != nil { + return nil, nil, nil, err + } + _ = alicePool.Start() + + bobPool := lnwallet.NewSigPool(1, bobSigner) + channelBob, err := lnwallet.NewLightningChannel( + bobSigner, bobChannelState, bobPool, + ) + if err != nil { + return nil, nil, nil, err + } + _ = bobPool.Start() + + chainIO := &mockChainIO{ + bestHeight: broadcastHeight, + } + wallet := &lnwallet.LightningWallet{ + WalletController: &mockWalletController{ + rootKey: aliceKeyPriv, + publishedTxns: publTx, + }, + } + + _, currentHeight, err := chainIO.GetBestBlock() + if err != nil { + return nil, nil, nil, err + } + + htlcSwitch, err := htlcswitch.New(htlcswitch.Config{ + DB: dbAlice, + SwitchPackager: channeldb.NewSwitchPackager(), + Notifier: notifier, + FwdEventTicker: ticker.New( + htlcswitch.DefaultFwdEventInterval), + LogEventTicker: ticker.New( + htlcswitch.DefaultLogInterval), + AckEventTicker: ticker.New( + htlcswitch.DefaultAckInterval), + }, uint32(currentHeight)) + if err != nil { + return nil, nil, nil, err + } + if err = htlcSwitch.Start(); err != nil { + return nil, nil, nil, err + } + + nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner) + + const chanActiveTimeout = time.Minute + + chanStatusMgr, err := netann.NewChanStatusManager(&netann.ChanStatusConfig{ + ChanStatusSampleInterval: 30 * time.Second, + ChanEnableTimeout: chanActiveTimeout, + ChanDisableTimeout: 2 * time.Minute, + DB: dbAlice, + Graph: dbAlice.ChannelGraph(), + MessageSigner: nodeSignerAlice, + OurPubKey: aliceKeyPub, + IsChannelActive: htlcSwitch.HasActiveLink, + ApplyChannelUpdate: func(*lnwire.ChannelUpdate) error { return nil }, + }) + if err != nil { + return nil, nil, nil, err + } + if err = chanStatusMgr.Start(); err != nil { + return nil, nil, nil, err + } + + errBuffer, err := queue.NewCircularBuffer(ErrorBufferSize) + if err != nil { + return nil, nil, nil, err + } + + var pubKey [33]byte + copy(pubKey[:], aliceKeyPub.SerializeCompressed()) + + cfgAddr := &lnwire.NetAddress{ + IdentityKey: aliceKeyPub, + Address: aliceAddr, + ChainNet: wire.SimNet, + } + + cfg := &Config{ + Addr: cfgAddr, + PubKeyBytes: pubKey, + ErrorBuffer: errBuffer, + ChainIO: chainIO, + Switch: htlcSwitch, + + ChanActiveTimeout: chanActiveTimeout, + InterceptSwitch: htlcswitch.NewInterceptableSwitch(htlcSwitch), + + ChannelDB: dbAlice, + FeeEstimator: estimator, + Wallet: wallet, + ChainNotifier: notifier, + ChanStatusMgr: chanStatusMgr, + DisconnectPeer: func(b *btcec.PublicKey) error { return nil }, + } + + alicePeer := NewBrontide(*cfg) + + chanID := lnwire.NewChanIDFromOutPoint(channelAlice.ChannelPoint()) + alicePeer.activeChannels[chanID] = channelAlice + + alicePeer.wg.Add(1) + go alicePeer.channelManager() + + return alicePeer, channelBob, cleanUpFunc, nil +} diff --git a/rpcserver.go b/rpcserver.go index 2b161f7bd..be27c48ab 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -57,6 +57,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/monitoring" + "github.com/lightningnetwork/lnd/peer" "github.com/lightningnetwork/lnd/peernotifier" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing" @@ -2105,17 +2106,17 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, // With the transaction broadcast, we send our first update to // the client. updateChan = make(chan interface{}, 2) - updateChan <- &pendingUpdate{ + updateChan <- &peer.PendingUpdate{ Txid: closingTxid[:], } errChan = make(chan error, 1) notifier := r.server.cc.chainNotifier - go waitForChanToClose(uint32(bestHeight), notifier, errChan, chanPoint, + go peer.WaitForChanToClose(uint32(bestHeight), notifier, errChan, chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() { // Respond to the local subsystem which // requested the channel closure. - updateChan <- &channelCloseUpdate{ + updateChan <- &peer.ChannelCloseUpdate{ ClosingTxid: closingTxid[:], Success: true, } @@ -2228,7 +2229,7 @@ out: // then we can break out of our dispatch loop as we no // longer need to process any further updates. switch closeUpdate := closingUpdate.(type) { - case *channelCloseUpdate: + case *peer.ChannelCloseUpdate: h, _ := chainhash.NewHash(closeUpdate.ClosingTxid) rpcsLog.Infof("[closechannel] close completed: "+ "txid(%v)", h) @@ -2246,7 +2247,7 @@ func createRPCCloseUpdate(update interface{}) ( *lnrpc.CloseStatusUpdate, error) { switch u := update.(type) { - case *channelCloseUpdate: + case *peer.ChannelCloseUpdate: return &lnrpc.CloseStatusUpdate{ Update: &lnrpc.CloseStatusUpdate_ChanClose{ ChanClose: &lnrpc.ChannelCloseUpdate{ @@ -2254,7 +2255,7 @@ func createRPCCloseUpdate(update interface{}) ( }, }, }, nil - case *pendingUpdate: + case *peer.PendingUpdate: return &lnrpc.CloseStatusUpdate{ Update: &lnrpc.CloseStatusUpdate_ClosePending{ ClosePending: &lnrpc.PendingUpdate{ @@ -2571,12 +2572,12 @@ func (r *rpcServer) ListPeers(ctx context.Context, serverPeer.RemoteFeatures(), ) - peer := &lnrpc.Peer{ + rpcPeer := &lnrpc.Peer{ PubKey: hex.EncodeToString(nodePub[:]), - Address: serverPeer.conn.RemoteAddr().String(), - Inbound: serverPeer.inbound, - BytesRecv: atomic.LoadUint64(&serverPeer.bytesReceived), - BytesSent: atomic.LoadUint64(&serverPeer.bytesSent), + Address: serverPeer.Conn().RemoteAddr().String(), + Inbound: serverPeer.Inbound(), + BytesRecv: serverPeer.BytesReceived(), + BytesSent: serverPeer.BytesSent(), SatSent: satSent, SatRecv: satRecv, PingTime: serverPeer.PingTime(), @@ -2591,27 +2592,27 @@ func (r *rpcServer) ListPeers(ctx context.Context, // it is non-nil. If we want all the stored errors, simply // add the full list to our set of errors. if in.LatestError { - latestErr := serverPeer.errorBuffer.Latest() + latestErr := serverPeer.ErrorBuffer().Latest() if latestErr != nil { peerErrors = []interface{}{latestErr} } } else { - peerErrors = serverPeer.errorBuffer.List() + peerErrors = serverPeer.ErrorBuffer().List() } // Add the relevant peer errors to our response. for _, error := range peerErrors { - tsError := error.(*timestampedError) + tsError := error.(*peer.TimestampedError) rpcErr := &lnrpc.TimestampedError{ - Timestamp: uint64(tsError.timestamp.Unix()), - Error: tsError.error.Error(), + Timestamp: uint64(tsError.Timestamp.Unix()), + Error: tsError.Error.Error(), } - peer.Errors = append(peer.Errors, rpcErr) + rpcPeer.Errors = append(rpcPeer.Errors, rpcErr) } - resp.Peers = append(resp.Peers, peer) + resp.Peers = append(resp.Peers, rpcPeer) } rpcsLog.Debugf("[listpeers] yielded %v peers", serverPeers) diff --git a/server.go b/server.go index 3cd357a03..0d1138311 100644 --- a/server.go +++ b/server.go @@ -52,6 +52,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/nat" "github.com/lightningnetwork/lnd/netann" + "github.com/lightningnetwork/lnd/peer" "github.com/lightningnetwork/lnd/peernotifier" "github.com/lightningnetwork/lnd/pool" "github.com/lightningnetwork/lnd/queue" @@ -113,7 +114,7 @@ var ( // errPeerAlreadyConnected is an error returned by the server when we're // commanded to connect to a peer, but they're already connected. type errPeerAlreadyConnected struct { - peer *peer + peer *peer.Brontide } // Error returns the human readable version of this error type. @@ -167,10 +168,10 @@ type server struct { lastDetectedIP net.IP mu sync.RWMutex - peersByPub map[string]*peer + peersByPub map[string]*peer.Brontide - inboundPeers map[string]*peer - outboundPeers map[string]*peer + inboundPeers map[string]*peer.Brontide + outboundPeers map[string]*peer.Brontide peerConnectedListeners map[string][]chan<- lnpeer.Peer peerDisconnectedListeners map[string][]chan<- struct{} @@ -190,7 +191,7 @@ type server struct { // a disconnect. Adding a peer to this map causes the peer termination // watcher to short circuit in the event that peers are purposefully // disconnected. - ignorePeerTermination map[*peer]struct{} + ignorePeerTermination map[*peer.Brontide]struct{} // scheduledPeerConnection maps a pubkey string to a callback that // should be executed in the peerTerminationWatcher the prior peer with @@ -452,12 +453,12 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, persistentConnReqs: make(map[string][]*connmgr.ConnReq), persistentRetryCancels: make(map[string]chan struct{}), peerErrors: make(map[string]*queue.CircularBuffer), - ignorePeerTermination: make(map[*peer]struct{}), + ignorePeerTermination: make(map[*peer.Brontide]struct{}), scheduledPeerConnection: make(map[string]func()), - peersByPub: make(map[string]*peer), - inboundPeers: make(map[string]*peer), - outboundPeers: make(map[string]*peer), + peersByPub: make(map[string]*peer.Brontide), + inboundPeers: make(map[string]*peer.Brontide), + outboundPeers: make(map[string]*peer.Brontide), peerConnectedListeners: make(map[string][]chan<- lnpeer.Peer), peerDisconnectedListeners: make(map[string][]chan<- struct{}), @@ -491,15 +492,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, return } - select { - case peer.localCloseChanReqs <- request: - srvrLog.Infof("Local close channel request "+ - "delivered to peer: %x", pubKey[:]) - case <-peer.quit: - srvrLog.Errorf("Unable to deliver local close "+ - "channel request to peer %x, err: %v", - pubKey[:], err) - } + peer.HandleLocalCloseChanReqs(request) }, FwdingLog: chanDB.ForwardingLog(), SwitchPackager: channeldb.NewSwitchPackager(), @@ -1481,7 +1474,13 @@ func (s *server) Stop() error { // Disconnect from each active peers to ensure that // peerTerminationWatchers signal completion to each peer. for _, peer := range s.Peers() { - s.DisconnectPeer(peer.addr.IdentityKey) + err := s.DisconnectPeer(peer.IdentityKey()) + if err != nil { + srvrLog.Warnf("could not disconnect peer: %v"+ + "received error: %v", peer.IdentityKey(), + err, + ) + } } // Now that all connections have been torn down, stop the tower @@ -1820,7 +1819,7 @@ func (s *server) peerBootstrapper(numTargetPeers uint32, s.mu.RLock() ignoreList := make(map[autopilot.NodeID]struct{}) for _, peer := range s.peersByPub { - nID := autopilot.NewNodeID(peer.addr.IdentityKey) + nID := autopilot.NewNodeID(peer.IdentityKey()) ignoreList[nID] = struct{}{} } s.mu.RUnlock() @@ -2310,12 +2309,12 @@ func (s *server) BroadcastMessage(skips map[route.Vertex]struct{}, // peersByPub throughout this process to ensure we deliver messages to // exact set of peers present at the time of invocation. s.mu.RLock() - peers := make([]*peer, 0, len(s.peersByPub)) + peers := make([]*peer.Brontide, 0, len(s.peersByPub)) for _, sPeer := range s.peersByPub { if skips != nil { - if _, ok := skips[sPeer.pubKeyBytes]; ok { + if _, ok := skips[sPeer.PubKey()]; ok { srvrLog.Tracef("Skipping %x in broadcast", - sPeer.pubKeyBytes[:]) + sPeer.PubKey()) continue } } @@ -2413,7 +2412,7 @@ func (s *server) NotifyWhenOffline(peerPubKey [33]byte) <-chan struct{} { // daemon's local representation of the remote peer. // // NOTE: This function is safe for concurrent access. -func (s *server) FindPeer(peerKey *btcec.PublicKey) (*peer, error) { +func (s *server) FindPeer(peerKey *btcec.PublicKey) (*peer.Brontide, error) { s.mu.RLock() defer s.mu.RUnlock() @@ -2427,7 +2426,7 @@ func (s *server) FindPeer(peerKey *btcec.PublicKey) (*peer, error) { // public key. // // NOTE: This function is safe for concurrent access. -func (s *server) FindPeerByPubStr(pubStr string) (*peer, error) { +func (s *server) FindPeerByPubStr(pubStr string) (*peer.Brontide, error) { s.mu.RLock() defer s.mu.RUnlock() @@ -2436,7 +2435,7 @@ func (s *server) FindPeerByPubStr(pubStr string) (*peer, error) { // findPeerByPubStr is an internal method that retrieves the specified peer from // the server's internal state using. -func (s *server) findPeerByPubStr(pubStr string) (*peer, error) { +func (s *server) findPeerByPubStr(pubStr string) (*peer.Brontide, error) { peer, ok := s.peersByPub[pubStr] if !ok { return nil, ErrPeerNotConnected @@ -2565,7 +2564,7 @@ func (s *server) InboundPeerConnected(conn net.Conn) { // we'll close out the new connection s.t there's only a single // connection between us. localPub := s.identityECDH.PubKey() - if !connectedPeer.inbound && + if !connectedPeer.Inbound() && !shouldDropLocalConnection(localPub, nodePub) { srvrLog.Warnf("Received inbound connection from "+ @@ -2676,7 +2675,7 @@ func (s *server) OutboundPeerConnected(connReq *connmgr.ConnReq, conn net.Conn) // we'll close out the new connection s.t there's only a single // connection between us. localPub := s.identityECDH.PubKey() - if connectedPeer.inbound && + if connectedPeer.Inbound() && shouldDropLocalConnection(localPub, nodePub) { srvrLog.Warnf("Established outbound connection to "+ @@ -2786,7 +2785,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, errBuffer, ok := s.peerErrors[pkStr] if !ok { var err error - errBuffer, err = queue.NewCircularBuffer(errorBufferSize) + errBuffer, err = queue.NewCircularBuffer(peer.ErrorBufferSize) if err != nil { srvrLog.Errorf("unable to create peer %v", err) return @@ -2799,16 +2798,63 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, // offered that would trigger channel closure. In case of outgoing // htlcs, an extra block is added to prevent the channel from being // closed when the htlc is outstanding and a new block comes in. - p, err := newPeer( - s.cfg, conn, connReq, s, peerAddr, inbound, initFeatures, - legacyFeatures, s.cfg.ChanEnableTimeout, - lncfg.DefaultOutgoingCltvRejectDelta, errBuffer, - ) - if err != nil { - srvrLog.Errorf("unable to create peer %v", err) - return + pCfg := peer.Config{ + Conn: conn, + ConnReq: connReq, + Addr: peerAddr, + Inbound: inbound, + Features: initFeatures, + LegacyFeatures: legacyFeatures, + OutgoingCltvRejectDelta: lncfg.DefaultOutgoingCltvRejectDelta, + ChanActiveTimeout: s.cfg.ChanEnableTimeout, + ErrorBuffer: errBuffer, + WritePool: s.writePool, + ReadPool: s.readPool, + Switch: s.htlcSwitch, + InterceptSwitch: s.interceptableSwitch, + ChannelDB: s.chanDB, + ChainArb: s.chainArb, + AuthGossiper: s.authGossiper, + ChanStatusMgr: s.chanStatusMgr, + ChainIO: s.cc.chainIO, + FeeEstimator: s.cc.feeEstimator, + Signer: s.cc.wallet.Cfg.Signer, + SigPool: s.sigPool, + Wallet: s.cc.wallet, + ChainNotifier: s.cc.chainNotifier, + RoutingPolicy: s.cc.routingPolicy, + Sphinx: s.sphinx, + WitnessBeacon: s.witnessBeacon, + Invoices: s.invoices, + ChannelNotifier: s.channelNotifier, + HtlcNotifier: s.htlcNotifier, + TowerClient: s.towerClient, + DisconnectPeer: s.DisconnectPeer, + GenNodeAnnouncement: s.genNodeAnnouncement, + + PrunePersistentPeerConnection: s.prunePersistentPeerConnection, + + FetchLastChanUpdate: s.fetchLastChanUpdate(), + ProcessFundingOpen: s.fundingMgr.processFundingOpen, + ProcessFundingAccept: s.fundingMgr.processFundingAccept, + ProcessFundingCreated: s.fundingMgr.processFundingCreated, + ProcessFundingSigned: s.fundingMgr.processFundingSigned, + ProcessFundingLocked: s.fundingMgr.processFundingLocked, + ProcessFundingError: s.fundingMgr.processFundingError, + IsPendingChannel: s.fundingMgr.IsPendingChannel, + + Hodl: s.cfg.Hodl, + UnsafeReplay: s.cfg.UnsafeReplay, + MaxOutgoingCltvExpiry: s.cfg.MaxOutgoingCltvExpiry, + MaxChannelFeeAllocation: s.cfg.MaxChannelFeeAllocation, + Quit: s.quit, } + copy(pCfg.PubKeyBytes[:], peerAddr.IdentityKey.SerializeCompressed()) + copy(pCfg.ServerPubKey[:], s.identityECDH.PubKey().SerializeCompressed()) + + p := peer.NewBrontide(pCfg) + // TODO(roasbeef): update IP address for link-node // * also mark last-seen, do it one single transaction? @@ -2828,7 +2874,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, // addPeer adds the passed peer to the server's global state of all active // peers. -func (s *server) addPeer(p *peer) { +func (s *server) addPeer(p *peer.Brontide) { if p == nil { return } @@ -2844,12 +2890,12 @@ func (s *server) addPeer(p *peer) { // TODO(roasbeef): pipe all requests through to the // queryHandler/peerManager - pubSer := p.addr.IdentityKey.SerializeCompressed() + pubSer := p.IdentityKey().SerializeCompressed() pubStr := string(pubSer) s.peersByPub[pubStr] = p - if p.inbound { + if p.Inbound() { s.inboundPeers[pubStr] = p } else { s.outboundPeers[pubStr] = p @@ -2872,7 +2918,7 @@ func (s *server) addPeer(p *peer) { // be signaled of the new peer once the method returns. // // NOTE: This MUST be launched as a goroutine. -func (s *server) peerInitializer(p *peer) { +func (s *server) peerInitializer(p *peer.Brontide) { defer s.wg.Done() // Avoid initializing peers while the server is exiting. @@ -2905,7 +2951,7 @@ func (s *server) peerInitializer(p *peer) { // was successful, and to begin watching the peer's wait group. close(ready) - pubStr := string(p.addr.IdentityKey.SerializeCompressed()) + pubStr := string(p.IdentityKey().SerializeCompressed()) s.mu.Lock() defer s.mu.Unlock() @@ -2933,7 +2979,7 @@ func (s *server) peerInitializer(p *peer) { // successfully, otherwise the peer should be disconnected instead. // // NOTE: This MUST be launched as a goroutine. -func (s *server) peerTerminationWatcher(p *peer, ready chan struct{}) { +func (s *server) peerTerminationWatcher(p *peer.Brontide, ready chan struct{}) { defer s.wg.Done() p.WaitForDisconnect(ready) @@ -2952,7 +2998,7 @@ func (s *server) peerTerminationWatcher(p *peer, ready chan struct{}) { // available for use. s.fundingMgr.CancelPeerReservations(p.PubKey()) - pubKey := p.addr.IdentityKey + pubKey := p.IdentityKey() // We'll also inform the gossiper that this peer is no longer active, // so we don't need to maintain sync state for it any longer. @@ -2963,13 +3009,13 @@ func (s *server) peerTerminationWatcher(p *peer, ready chan struct{}) { // with this interface should be closed. // // TODO(roasbeef): instead add a PurgeInterfaceLinks function? - links, err := p.server.htlcSwitch.GetLinksByInterface(p.pubKeyBytes) + links, err := s.htlcSwitch.GetLinksByInterface(p.PubKey()) if err != nil && err != htlcswitch.ErrNoLinksFound { srvrLog.Errorf("Unable to get channel links for %v: %v", p, err) } for _, link := range links { - p.server.htlcSwitch.RemoveLink(link.ChanID()) + s.htlcSwitch.RemoveLink(link.ChanID()) } s.mu.Lock() @@ -3022,12 +3068,12 @@ func (s *server) peerTerminationWatcher(p *peer, ready chan struct{}) { // within the peer's address for reconnection purposes. // // TODO(roasbeef): use them all? - if p.inbound { + if p.Inbound() { advertisedAddr, err := s.fetchNodeAdvertisedAddr(pubKey) switch { // We found an advertised address, so use it. case err == nil: - p.addr.Address = advertisedAddr + p.SetAddress(advertisedAddr) // The peer doesn't have an advertised address. case err == errNoAdvertisedAddr: @@ -3060,7 +3106,7 @@ func (s *server) peerTerminationWatcher(p *peer, ready chan struct{}) { // Otherwise, we'll launch a new connection request in order to // attempt to maintain a persistent connection with this peer. connReq := &connmgr.ConnReq{ - Addr: p.addr, + Addr: p.NetAddress(), Permanent: true, } s.persistentConnReqs[pubStr] = append( @@ -3103,7 +3149,7 @@ func (s *server) peerTerminationWatcher(p *peer, ready chan struct{}) { // removePeer removes the passed peer from the server's state of all active // peers. -func (s *server) removePeer(p *peer) { +func (s *server) removePeer(p *peer.Brontide) { if p == nil { return } @@ -3115,8 +3161,8 @@ func (s *server) removePeer(p *peer) { p.Disconnect(fmt.Errorf("server: disconnecting peer %v", p)) // If this peer had an active persistent connection request, remove it. - if p.connReq != nil { - s.connMgr.Remove(p.connReq.ID()) + if p.ConnReq() != nil { + s.connMgr.Remove(p.ConnReq().ID()) } // Ignore deleting peers if we're shutting down. @@ -3124,12 +3170,13 @@ func (s *server) removePeer(p *peer) { return } - pubSer := p.addr.IdentityKey.SerializeCompressed() + pKey := p.PubKey() + pubSer := pKey[:] pubStr := string(pubSer) delete(s.peersByPub, pubStr) - if p.inbound { + if p.Inbound() { delete(s.inboundPeers, pubStr) } else { delete(s.outboundPeers, pubStr) @@ -3137,8 +3184,8 @@ func (s *server) removePeer(p *peer) { // Copy the peer's error buffer across to the server if it has any items // in it so that we can restore peer errors across connections. - if p.errorBuffer.Total() > 0 { - s.peerErrors[pubStr] = p.errorBuffer + if p.ErrorBuffer().Total() > 0 { + s.peerErrors[pubStr] = p.ErrorBuffer() } // Inform the peer notifier of a peer offline event so that it can be @@ -3358,8 +3405,8 @@ func (s *server) OpenChannel( // We'll wait until the peer is active before beginning the channel // opening process. select { - case <-peer.activeSignal: - case <-peer.quit: + case <-peer.ActiveSignal(): + case <-peer.QuitSignal(): req.err <- fmt.Errorf("peer %x disconnected", pubKeyBytes) return req.updates, req.err case <-s.quit: @@ -3391,11 +3438,11 @@ func (s *server) OpenChannel( // Peers returns a slice of all active peers. // // NOTE: This function is safe for concurrent access. -func (s *server) Peers() []*peer { +func (s *server) Peers() []*peer.Brontide { s.mu.RLock() defer s.mu.RUnlock() - peers := make([]*peer, 0, len(s.peersByPub)) + peers := make([]*peer.Brontide, 0, len(s.peersByPub)) for _, peer := range s.peersByPub { peers = append(peers, peer) } diff --git a/test_utils.go b/test_utils.go index 0e032ee09..78c2601e8 100644 --- a/test_utils.go +++ b/test_utils.go @@ -1,34 +1,8 @@ package lnd import ( - "bytes" - crand "crypto/rand" - "encoding/binary" - "io" - "io/ioutil" - "math/rand" - "net" - "os" - "time" - - "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/lightningnetwork/lnd/chainntnfs" - "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/clock" - "github.com/lightningnetwork/lnd/contractcourt" - "github.com/lightningnetwork/lnd/htlcswitch" - "github.com/lightningnetwork/lnd/input" - "github.com/lightningnetwork/lnd/keychain" - "github.com/lightningnetwork/lnd/lnwallet" - "github.com/lightningnetwork/lnd/lnwallet/chainfee" - "github.com/lightningnetwork/lnd/lnwallet/chancloser" - "github.com/lightningnetwork/lnd/lnwire" - "github.com/lightningnetwork/lnd/netann" - "github.com/lightningnetwork/lnd/shachain" - "github.com/lightningnetwork/lnd/ticker" ) var ( @@ -54,9 +28,6 @@ var ( 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, } - // Just use some arbitrary bytes as delivery script. - dummyDeliveryScript = alicesPrivKey[:] - // testTx is used as the default funding txn for single-funder channels. testTx = &wire.MsgTx{ Version: 1, @@ -91,372 +62,3 @@ var ( LockTime: 5, } ) - -// noUpdate is a function which can be used as a parameter in createTestPeer to -// call the setup code with no custom values on the channels set up. -var noUpdate = func(a, b *channeldb.OpenChannel) {} - -// createTestPeer creates a channel between two nodes, and returns a peer for -// one of the nodes, together with the channel seen from both nodes. It takes -// an updateChan function which can be used to modify the default values on -// the channel states for each peer. -func createTestPeer(notifier chainntnfs.ChainNotifier, publTx chan *wire.MsgTx, - updateChan func(a, b *channeldb.OpenChannel)) (*peer, *lnwallet.LightningChannel, - *lnwallet.LightningChannel, func(), error) { - - aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes( - btcec.S256(), alicesPrivKey, - ) - aliceKeySigner := &keychain.PrivKeyDigestSigner{PrivKey: aliceKeyPriv} - bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes( - btcec.S256(), bobsPrivKey, - ) - - channelCapacity := btcutil.Amount(10 * 1e8) - channelBal := channelCapacity / 2 - aliceDustLimit := btcutil.Amount(200) - bobDustLimit := btcutil.Amount(1300) - csvTimeoutAlice := uint32(5) - csvTimeoutBob := uint32(4) - - prevOut := &wire.OutPoint{ - Hash: chainhash.Hash(testHdSeed), - Index: 0, - } - fundingTxIn := wire.NewTxIn(prevOut, nil, nil) - - aliceCfg := channeldb.ChannelConfig{ - ChannelConstraints: channeldb.ChannelConstraints{ - DustLimit: aliceDustLimit, - MaxPendingAmount: lnwire.MilliSatoshi(rand.Int63()), - ChanReserve: btcutil.Amount(rand.Int63()), - MinHTLC: lnwire.MilliSatoshi(rand.Int63()), - MaxAcceptedHtlcs: uint16(rand.Int31()), - CsvDelay: uint16(csvTimeoutAlice), - }, - MultiSigKey: keychain.KeyDescriptor{ - PubKey: aliceKeyPub, - }, - RevocationBasePoint: keychain.KeyDescriptor{ - PubKey: aliceKeyPub, - }, - PaymentBasePoint: keychain.KeyDescriptor{ - PubKey: aliceKeyPub, - }, - DelayBasePoint: keychain.KeyDescriptor{ - PubKey: aliceKeyPub, - }, - HtlcBasePoint: keychain.KeyDescriptor{ - PubKey: aliceKeyPub, - }, - } - bobCfg := channeldb.ChannelConfig{ - ChannelConstraints: channeldb.ChannelConstraints{ - DustLimit: bobDustLimit, - MaxPendingAmount: lnwire.MilliSatoshi(rand.Int63()), - ChanReserve: btcutil.Amount(rand.Int63()), - MinHTLC: lnwire.MilliSatoshi(rand.Int63()), - MaxAcceptedHtlcs: uint16(rand.Int31()), - CsvDelay: uint16(csvTimeoutBob), - }, - MultiSigKey: keychain.KeyDescriptor{ - PubKey: bobKeyPub, - }, - RevocationBasePoint: keychain.KeyDescriptor{ - PubKey: bobKeyPub, - }, - PaymentBasePoint: keychain.KeyDescriptor{ - PubKey: bobKeyPub, - }, - DelayBasePoint: keychain.KeyDescriptor{ - PubKey: bobKeyPub, - }, - HtlcBasePoint: keychain.KeyDescriptor{ - PubKey: bobKeyPub, - }, - } - - bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize()) - if err != nil { - return nil, nil, nil, nil, err - } - bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) - bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) - if err != nil { - return nil, nil, nil, nil, err - } - bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:]) - - aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize()) - if err != nil { - return nil, nil, nil, nil, err - } - alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) - aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) - if err != nil { - return nil, nil, nil, nil, err - } - aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) - - aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns( - channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, - bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit, - ) - if err != nil { - return nil, nil, nil, nil, err - } - - alicePath, err := ioutil.TempDir("", "alicedb") - if err != nil { - return nil, nil, nil, nil, err - } - - dbAlice, err := channeldb.Open(alicePath) - if err != nil { - return nil, nil, nil, nil, err - } - - bobPath, err := ioutil.TempDir("", "bobdb") - if err != nil { - return nil, nil, nil, nil, err - } - - dbBob, err := channeldb.Open(bobPath) - if err != nil { - return nil, nil, nil, nil, err - } - - estimator := chainfee.NewStaticEstimator(12500, 0) - feePerKw, err := estimator.EstimateFeePerKW(1) - if err != nil { - return nil, nil, nil, nil, err - } - - // TODO(roasbeef): need to factor in commit fee? - aliceCommit := channeldb.ChannelCommitment{ - CommitHeight: 0, - LocalBalance: lnwire.NewMSatFromSatoshis(channelBal), - RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal), - FeePerKw: btcutil.Amount(feePerKw), - CommitFee: feePerKw.FeeForWeight(input.CommitWeight), - CommitTx: aliceCommitTx, - CommitSig: bytes.Repeat([]byte{1}, 71), - } - bobCommit := channeldb.ChannelCommitment{ - CommitHeight: 0, - LocalBalance: lnwire.NewMSatFromSatoshis(channelBal), - RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal), - FeePerKw: btcutil.Amount(feePerKw), - CommitFee: feePerKw.FeeForWeight(input.CommitWeight), - CommitTx: bobCommitTx, - CommitSig: bytes.Repeat([]byte{1}, 71), - } - - var chanIDBytes [8]byte - if _, err := io.ReadFull(crand.Reader, chanIDBytes[:]); err != nil { - return nil, nil, nil, nil, err - } - - shortChanID := lnwire.NewShortChanIDFromInt( - binary.BigEndian.Uint64(chanIDBytes[:]), - ) - - aliceChannelState := &channeldb.OpenChannel{ - LocalChanCfg: aliceCfg, - RemoteChanCfg: bobCfg, - IdentityPub: aliceKeyPub, - FundingOutpoint: *prevOut, - ShortChannelID: shortChanID, - ChanType: channeldb.SingleFunderTweaklessBit, - IsInitiator: true, - Capacity: channelCapacity, - RemoteCurrentRevocation: bobCommitPoint, - RevocationProducer: alicePreimageProducer, - RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: aliceCommit, - RemoteCommitment: aliceCommit, - Db: dbAlice, - Packager: channeldb.NewChannelPackager(shortChanID), - FundingTxn: testTx, - } - bobChannelState := &channeldb.OpenChannel{ - LocalChanCfg: bobCfg, - RemoteChanCfg: aliceCfg, - IdentityPub: bobKeyPub, - FundingOutpoint: *prevOut, - ChanType: channeldb.SingleFunderTweaklessBit, - IsInitiator: false, - Capacity: channelCapacity, - RemoteCurrentRevocation: aliceCommitPoint, - RevocationProducer: bobPreimageProducer, - RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: bobCommit, - RemoteCommitment: bobCommit, - Db: dbBob, - Packager: channeldb.NewChannelPackager(shortChanID), - } - - // Set custom values on the channel states. - updateChan(aliceChannelState, bobChannelState) - - aliceAddr := &net.TCPAddr{ - IP: net.ParseIP("127.0.0.1"), - Port: 18555, - } - - if err := aliceChannelState.SyncPending(aliceAddr, 0); err != nil { - return nil, nil, nil, nil, err - } - - bobAddr := &net.TCPAddr{ - IP: net.ParseIP("127.0.0.1"), - Port: 18556, - } - - if err := bobChannelState.SyncPending(bobAddr, 0); err != nil { - return nil, nil, nil, nil, err - } - - cleanUpFunc := func() { - os.RemoveAll(bobPath) - os.RemoveAll(alicePath) - } - - aliceSigner := &mockSigner{aliceKeyPriv} - bobSigner := &mockSigner{bobKeyPriv} - - alicePool := lnwallet.NewSigPool(1, aliceSigner) - channelAlice, err := lnwallet.NewLightningChannel( - aliceSigner, aliceChannelState, alicePool, - ) - if err != nil { - return nil, nil, nil, nil, err - } - alicePool.Start() - - bobPool := lnwallet.NewSigPool(1, bobSigner) - channelBob, err := lnwallet.NewLightningChannel( - bobSigner, bobChannelState, bobPool, - ) - if err != nil { - return nil, nil, nil, nil, err - } - bobPool.Start() - - chainIO := &mockChainIO{ - bestHeight: fundingBroadcastHeight, - } - wallet := &lnwallet.LightningWallet{ - WalletController: &mockWalletController{ - rootKey: aliceKeyPriv, - publishedTransactions: publTx, - }, - } - cc := &chainControl{ - feeEstimator: estimator, - chainIO: chainIO, - chainNotifier: notifier, - wallet: wallet, - } - - breachArbiter := &breachArbiter{} - - chainArb := contractcourt.NewChainArbitrator( - contractcourt.ChainArbitratorConfig{ - Notifier: notifier, - ChainIO: chainIO, - IsForwardedHTLC: func(chanID lnwire.ShortChannelID, - htlcIndex uint64) bool { - - return true - }, - Clock: clock.NewDefaultClock(), - }, dbAlice, - ) - chainArb.WatchNewChannel(aliceChannelState) - - s := &server{ - chanDB: dbAlice, - cc: cc, - breachArbiter: breachArbiter, - chainArb: chainArb, - } - - _, currentHeight, err := s.cc.chainIO.GetBestBlock() - if err != nil { - return nil, nil, nil, nil, err - } - - htlcSwitch, err := htlcswitch.New(htlcswitch.Config{ - DB: dbAlice, - SwitchPackager: channeldb.NewSwitchPackager(), - Notifier: notifier, - FwdEventTicker: ticker.New( - htlcswitch.DefaultFwdEventInterval), - LogEventTicker: ticker.New( - htlcswitch.DefaultLogInterval), - AckEventTicker: ticker.New( - htlcswitch.DefaultAckInterval), - }, uint32(currentHeight)) - if err != nil { - return nil, nil, nil, nil, err - } - if err = htlcSwitch.Start(); err != nil { - return nil, nil, nil, nil, err - } - s.htlcSwitch = htlcSwitch - - nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner) - - const chanActiveTimeout = time.Minute - - chanStatusMgr, err := netann.NewChanStatusManager(&netann.ChanStatusConfig{ - ChanStatusSampleInterval: 30 * time.Second, - ChanEnableTimeout: chanActiveTimeout, - ChanDisableTimeout: 2 * time.Minute, - DB: dbAlice, - Graph: dbAlice.ChannelGraph(), - MessageSigner: nodeSignerAlice, - OurPubKey: aliceKeyPub, - IsChannelActive: s.htlcSwitch.HasActiveLink, - ApplyChannelUpdate: func(*lnwire.ChannelUpdate) error { return nil }, - }) - if err != nil { - return nil, nil, nil, nil, err - } - if err = chanStatusMgr.Start(); err != nil { - return nil, nil, nil, nil, err - } - s.chanStatusMgr = chanStatusMgr - - alicePeer := &peer{ - addr: &lnwire.NetAddress{ - IdentityKey: aliceKeyPub, - Address: aliceAddr, - }, - - server: s, - sendQueue: make(chan outgoingMsg, 1), - outgoingQueue: make(chan outgoingMsg, outgoingQueueLen), - - activeChannels: make(map[lnwire.ChannelID]*lnwallet.LightningChannel), - newChannels: make(chan *newChannelMsg, 1), - - activeChanCloses: make(map[lnwire.ChannelID]*chancloser.ChanCloser), - localCloseChanReqs: make(chan *htlcswitch.ChanClose), - chanCloseMsgs: make(chan *closeMsg), - - chanActiveTimeout: chanActiveTimeout, - - queueQuit: make(chan struct{}), - quit: make(chan struct{}), - } - - chanID := lnwire.NewChanIDFromOutPoint(channelAlice.ChannelPoint()) - alicePeer.activeChannels[chanID] = channelAlice - - alicePeer.wg.Add(1) - go alicePeer.channelManager() - - return alicePeer, channelAlice, channelBob, cleanUpFunc, nil -}