funding: cleanup of 6 conf and 'private' channel logic

This commits slightly rewrites the newly introduced
logic for private channels. Instead of keeping the
channel announce preference in a database within
fundingManager, it is stored as part of the
OpenChannel struct.

In addition, the ChanOpenStatus_Open update is now
sent after the channel is added to the router, instead
of waiting until the 6 blocks confirmation has passed.
This commit is contained in:
Johan T. Halseth 2017-11-13 17:20:05 -08:00 committed by Olaoluwa Osuntokun
parent fffe15f0fd
commit 9af7cc9b99

@ -21,6 +21,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcd/wire"
@ -182,10 +183,10 @@ type fundingConfig struct {
// announcement from the backing Lighting Network node. // announcement from the backing Lighting Network node.
CurrentNodeAnnouncement func() (lnwire.NodeAnnouncement, error) CurrentNodeAnnouncement func() (lnwire.NodeAnnouncement, error)
// SendLocalAnnouncement is used by the FundingManager to send // SendAnnouncement is used by the FundingManager to send
// announcement messages to the Gossiper to possibly broadcast // announcement messages to the Gossiper to possibly broadcast
// to the greater network. // to the greater network.
SendLocalAnnouncement func(msg lnwire.Message) error SendAnnouncement func(msg lnwire.Message) error
// SendToPeer allows the FundingManager to send messages to the peer // SendToPeer allows the FundingManager to send messages to the peer
// node during the multiple steps involved in the creation of the // node during the multiple steps involved in the creation of the
@ -261,10 +262,6 @@ type fundingManager struct {
// funding workflows. // funding workflows.
activeReservations map[serializedPubKey]pendingChannels activeReservations map[serializedPubKey]pendingChannels
// pendingChanAnnPrefs is a map which stores a pending channel's id
// along with its announcement preference.
pendingChanAnnPrefs map[[32]byte]bool
// signedReservations is a utility map that maps the permanent channel // signedReservations is a utility map that maps the permanent channel
// ID of a funding reservation to its temporary channel ID. This is // ID of a funding reservation to its temporary channel ID. This is
// required as mid funding flow, we switch to referencing the channel // required as mid funding flow, we switch to referencing the channel
@ -334,12 +331,6 @@ var (
// of being opened. // of being opened.
channelOpeningStateBucket = []byte("channelOpeningState") channelOpeningStateBucket = []byte("channelOpeningState")
// openChanAnnPrefBucket is the database bucket used to store each
// channel's announcement preference signalled by the LSB of
// channel_flags of the open_channel message in the funding workflow.
// It only stores open channels' announcement preferences.
openChanAnnPrefBucket = []byte("open_chan_ann")
// ErrChannelNotFound is returned when we are looking for a specific // ErrChannelNotFound is returned when we are looking for a specific
// channel opening state in the FundingManager's internal database, but // channel opening state in the FundingManager's internal database, but
// the channel in question is not considered being in an opening state. // the channel in question is not considered being in an opening state.
@ -359,7 +350,6 @@ func newFundingManager(cfg fundingConfig) (*fundingManager, error) {
fundingRequests: make(chan *initFundingMsg, msgBufferSize), fundingRequests: make(chan *initFundingMsg, msgBufferSize),
localDiscoverySignals: make(map[lnwire.ChannelID]chan struct{}), localDiscoverySignals: make(map[lnwire.ChannelID]chan struct{}),
handleFundingLockedBarriers: make(map[lnwire.ChannelID]struct{}), handleFundingLockedBarriers: make(map[lnwire.ChannelID]struct{}),
pendingChanAnnPrefs: make(map[[32]byte]bool),
queries: make(chan interface{}, 1), queries: make(chan interface{}, 1),
quit: make(chan struct{}), quit: make(chan struct{}),
}, nil }, nil
@ -505,40 +495,22 @@ func (f *fundingManager) Start() error {
go func(dbChan *channeldb.OpenChannel) { go func(dbChan *channeldb.OpenChannel) {
defer f.wg.Done() defer f.wg.Done()
// Retrieve channel announcement preference err = f.addToRouterGraph(dbChan, shortChanID)
private, err := f.getOpenChanAnnPref(
&dbChan.FundingOutpoint)
if err != nil {
fndgLog.Errorf("unable to retrieve channel "+
"announcement preference: %v", err)
return
}
lnChannel, err := lnwallet.NewLightningChannel(
nil, nil, f.cfg.FeeEstimator, dbChan)
if err != nil {
fndgLog.Errorf("error creating "+
"lightning channel: %v", err)
return
}
defer lnChannel.Stop()
err = f.addToRouterGraph(dbChan, lnChannel,
shortChanID, private)
if err != nil { if err != nil {
fndgLog.Errorf("failed adding to "+ fndgLog.Errorf("failed adding to "+
"router graph: %v", err) "router graph: %v", err)
return return
} }
if !private { // TODO(halseth): should create a state machine
err = f.annAfterSixConfs(dbChan, // that can more easily be resumed from
lnChannel, shortChanID) // different states, to avoid this code
if err != nil { // duplication.
fndgLog.Errorf("error sending channel "+ err = f.annAfterSixConfs(dbChan, shortChanID)
"announcement: %v", err) if err != nil {
return fndgLog.Errorf("error sending channel "+
} "announcements: %v", err)
return
} }
}(channel) }(channel)
@ -548,43 +520,13 @@ func (f *fundingManager) Start() error {
f.wg.Add(1) f.wg.Add(1)
go func(dbChan *channeldb.OpenChannel) { go func(dbChan *channeldb.OpenChannel) {
defer f.wg.Done() defer f.wg.Done()
// Retrieve channel announcement preference
private, err := f.getOpenChanAnnPref(
&dbChan.FundingOutpoint)
if err != nil {
fndgLog.Errorf("unable to retrieve channel "+
"announcement preference: %v", err)
return
}
lnChannel, err := lnwallet.NewLightningChannel( err = f.annAfterSixConfs(channel, shortChanID)
nil, nil, f.cfg.FeeEstimator, dbChan)
if err != nil { if err != nil {
fndgLog.Errorf("error creating "+ fndgLog.Errorf("error sending channel "+
"lightning channel: %v", err) "announcement: %v", err)
return return
} }
defer lnChannel.Stop()
if private {
// We delete the channel from our internal
// database.
err := f.deleteChannelOpeningState(
&channel.FundingOutpoint)
if err != nil {
fndgLog.Errorf("error deleting "+
"channel state: %v", err)
return
}
} else {
err = f.annAfterSixConfs(channel,
lnChannel, shortChanID)
if err != nil {
fndgLog.Errorf("error sending channel "+
"announcement: %v", err)
return
}
}
}(channel) }(channel)
default: default:
@ -729,7 +671,6 @@ func (f *fundingManager) failFundingFlow(peer *btcec.PublicKey,
return return
} }
delete(f.pendingChanAnnPrefs, tempChanID)
f.cancelReservationCtx(peer, tempChanID) f.cancelReservationCtx(peer, tempChanID)
return return
} }
@ -826,6 +767,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
// number and send ErrorGeneric to remote peer if condition is // number and send ErrorGeneric to remote peer if condition is
// violated. // violated.
peerIDKey := newSerializedKey(fmsg.peerAddress.IdentityKey) peerIDKey := newSerializedKey(fmsg.peerAddress.IdentityKey)
msg := fmsg.msg msg := fmsg.msg
amt := msg.FundingAmount amt := msg.FundingAmount
@ -883,7 +825,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
reservation, err := f.cfg.Wallet.InitChannelReservation(amt, 0, reservation, err := f.cfg.Wallet.InitChannelReservation(amt, 0,
msg.PushAmount, btcutil.Amount(msg.FeePerKiloWeight), 0, msg.PushAmount, btcutil.Amount(msg.FeePerKiloWeight), 0,
fmsg.peerAddress.IdentityKey, fmsg.peerAddress.Address, fmsg.peerAddress.IdentityKey, fmsg.peerAddress.Address,
&chainHash) &chainHash, msg.ChannelFlags)
if err != nil { if err != nil {
fndgLog.Errorf("Unable to initialize reservation: %v", err) fndgLog.Errorf("Unable to initialize reservation: %v", err)
f.failFundingFlow(fmsg.peerAddress.IdentityKey, f.failFundingFlow(fmsg.peerAddress.IdentityKey,
@ -934,16 +876,6 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
} }
f.resMtx.Unlock() f.resMtx.Unlock()
// Save the announcement preference of this pending channel
if msg.ChannelFlags&1 == 0 {
// This channel WILL be announced to the greater network later.
f.pendingChanAnnPrefs[msg.PendingChannelID] = false
} else {
// This channel WILL NOT be announced to the greater network
// later.
f.pendingChanAnnPrefs[msg.PendingChannelID] = true
}
// Using the RequiredRemoteDelay closure, we'll compute the remote CSV // Using the RequiredRemoteDelay closure, we'll compute the remote CSV
// delay we require given the total amount of funds within the channel. // delay we require given the total amount of funds within the channel.
remoteCsvDelay := f.cfg.RequiredRemoteDelay(amt) remoteCsvDelay := f.cfg.RequiredRemoteDelay(amt)
@ -1178,12 +1110,6 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
peerKey, pendingChanID[:]) peerKey, pendingChanID[:])
return return
} }
private, ok := f.pendingChanAnnPrefs[pendingChanID]
if !ok {
fndgLog.Warnf("can't find channel announcement preference for"+
"chanID:%x", pendingChanID[:])
return
}
// The channel initiator has responded with the funding outpoint of the // The channel initiator has responded with the funding outpoint of the
// final funding transaction, as well as a signature for our version of // final funding transaction, as well as a signature for our version of
@ -1210,32 +1136,10 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
return return
} }
// A new channel has almost finished the funding process. In order to
// properly synchronize with the writeHandler goroutine, we add a new
// channel to the barriers map which will be closed once the channel is
// fully open.
f.barrierMtx.Lock()
channelID := lnwire.NewChanIDFromOutPoint(&fundingOut)
fndgLog.Debugf("Creating chan barrier for ChanID(%v)", channelID)
f.newChanBarriers[channelID] = make(chan struct{})
f.barrierMtx.Unlock()
// Store channel announcement preference in boltdb
if err = f.saveOpenChanAnnPref(&fundingOut, private); err != nil {
fndgLog.Errorf("unable to save channel announcement preference "+
"to db for chanID:%x", channelID)
}
// If something goes wrong before the funding transaction is confirmed, // If something goes wrong before the funding transaction is confirmed,
// we use this convenience method to delete the pending OpenChannel // we use this convenience method to delete the pending OpenChannel
// from the database. // from the database.
deleteFromDatabase := func() { deleteFromDatabase := func() {
err := f.deleteOpenChanAnnPref(&completeChan.FundingOutpoint)
if err != nil {
fndgLog.Errorf("Failed to delete channel announcement "+
"preference: %v", err)
}
closeInfo := &channeldb.ChannelCloseSummary{ closeInfo := &channeldb.ChannelCloseSummary{
ChanPoint: completeChan.FundingOutpoint, ChanPoint: completeChan.FundingOutpoint,
ChainHash: completeChan.ChainHash, ChainHash: completeChan.ChainHash,
@ -1318,31 +1222,36 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
go f.waitForFundingWithTimeout(completeChan, confChan, go f.waitForFundingWithTimeout(completeChan, confChan,
timeoutChan) timeoutChan)
var shortChanID *lnwire.ShortChannelID
var ok bool
select { select {
case <-timeoutChan: case <-timeoutChan:
// We did not see the funding confirmation before // We did not see the funding confirmation before
// timeout, so we forget the channel. // timeout, so we forget the channel.
deleteFromDatabase() deleteFromDatabase()
return
case <-f.quit: case <-f.quit:
// The fundingManager is shutting down, will resume // The fundingManager is shutting down, will resume
// wait for funding transaction on startup. // wait for funding transaction on startup.
case shortChanID, ok := <-confChan: return
case shortChanID, ok = <-confChan:
if !ok { if !ok {
fndgLog.Errorf("waiting for funding confirmation" + fndgLog.Errorf("waiting for funding confirmation" +
" failed") " failed")
return return
} }
// Fallthrough.
}
f.deleteReservationCtx(peerKey, fmsg.msg.PendingChannelID) // Success, funding transaction was confirmed.
f.deleteReservationCtx(peerKey, fmsg.msg.PendingChannelID)
// Success, funding transaction was confirmed. err := f.handleFundingConfirmation(completeChan,
err := f.handleFundingConfirmation(completeChan, shortChanID)
shortChanID) if err != nil {
if err != nil { fndgLog.Errorf("failed to handle funding"+
fndgLog.Errorf("failed to handle funding"+ "confirmation: %v", err)
"confirmation: %v", err) return
return
}
} }
}() }()
} }
@ -1391,12 +1300,6 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
pendingChanID, []byte(err.Error())) pendingChanID, []byte(err.Error()))
return return
} }
private, ok := f.pendingChanAnnPrefs[pendingChanID]
if !ok {
fndgLog.Warnf("can't find channel announcement preference for"+
"chanID:%x", pendingChanID[:])
return
}
// Create an entry in the local discovery map so we can ensure that we // Create an entry in the local discovery map so we can ensure that we
// process the channel confirmation fully before we receive a funding // process the channel confirmation fully before we receive a funding
@ -1407,12 +1310,6 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
f.localDiscoverySignals[permChanID] = make(chan struct{}) f.localDiscoverySignals[permChanID] = make(chan struct{})
f.localDiscoveryMtx.Unlock() f.localDiscoveryMtx.Unlock()
// Store channel announcement preference in boltdb
if err = f.saveOpenChanAnnPref(fundingPoint, private); err != nil {
fndgLog.Errorf("unable to save channel announcement preference "+
"to db for chanID:%x", permChanID)
}
// The remote peer has responded with a signature for our commitment // The remote peer has responded with a signature for our commitment
// transaction. We'll verify the signature for validity, then commit // transaction. We'll verify the signature for validity, then commit
// the state to disk as we can now open the channel. // the state to disk as we can now open the channel.
@ -1459,29 +1356,55 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
confChan) confChan)
}() }()
var shortChanID *lnwire.ShortChannelID
var ok bool
select { select {
case <-f.quit: case <-f.quit:
return return
case shortChanID, ok := <-confChan: case shortChanID, ok = <-confChan:
if !ok { if !ok {
fndgLog.Errorf("waiting for funding confirmation" + fndgLog.Errorf("waiting for funding confirmation" +
" failed") " failed")
return return
} }
f.deleteReservationCtx(peerKey, pendingChanID)
// Success, funding transaction was confirmed
err := f.handleFundingConfirmation(completeChan,
shortChanID)
if err != nil {
fndgLog.Errorf("failed to handle funding"+
"confirmation: %v", err)
return
}
} }
// Finally give the caller a final update notifying them that // Success, funding transaction was confirmed.
fndgLog.Debugf("Channel with ShortChanID %v now confirmed",
shortChanID.ToUint64())
// Go on adding the channel to the channel graph, and crafting
// channel announcements.
// We create the state-machine object which wraps the database state.
lnChannel, err := lnwallet.NewLightningChannel(nil, nil, f.cfg.FeeEstimator,
completeChan)
if err != nil {
fndgLog.Errorf("failed creating lnChannel: %v", err)
return
}
defer func() {
lnChannel.Stop()
lnChannel.CancelObserver()
}()
err = f.sendFundingLocked(completeChan, lnChannel, shortChanID)
if err != nil {
fndgLog.Errorf("failed sending fundingLocked: %v", err)
return
}
fndgLog.Debugf("FundingLocked for channel with ShortChanID "+
"%v sent", shortChanID.ToUint64())
err = f.addToRouterGraph(completeChan, shortChanID)
if err != nil {
fndgLog.Errorf("failed adding to router graph: %v", err)
return
}
fndgLog.Debugf("Channel with ShortChanID %v added to "+
"router graph", shortChanID.ToUint64())
// Give the caller a final update notifying them that
// the channel is now open. // the channel is now open.
// TODO(roasbeef): only notify after recv of funding locked? // TODO(roasbeef): only notify after recv of funding locked?
resCtx.updates <- &lnrpc.OpenStatusUpdate{ resCtx.updates <- &lnrpc.OpenStatusUpdate{
@ -1494,6 +1417,15 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
}, },
}, },
} }
f.deleteReservationCtx(peerKey, pendingChanID)
err = f.annAfterSixConfs(completeChan, shortChanID)
if err != nil {
fndgLog.Errorf("failed sending channel announcement: %v",
err)
return
}
}() }()
} }
@ -1672,8 +1604,9 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open
return return
} }
// This closes the discoverySignal channel, indicating to a separate // Close the discoverySignal channel, indicating to a separate
// goroutine that it is acceptable to process funding locked messages // goroutine that the channel now is marked as open in the database
// and that it is acceptable to process funding locked messages
// from the peer. // from the peer.
f.localDiscoveryMtx.Lock() f.localDiscoveryMtx.Lock()
if discoverySignal, ok := f.localDiscoverySignals[chanID]; ok { if discoverySignal, ok := f.localDiscoverySignals[chanID]; ok {
@ -1689,13 +1622,6 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open
func (f *fundingManager) handleFundingConfirmation(completeChan *channeldb.OpenChannel, func (f *fundingManager) handleFundingConfirmation(completeChan *channeldb.OpenChannel,
shortChanID *lnwire.ShortChannelID) error { shortChanID *lnwire.ShortChannelID) error {
// Retrieve channel announcement preference
private, err := f.getOpenChanAnnPref(&completeChan.FundingOutpoint)
if err != nil {
return fmt.Errorf("unable to retrieve channel announcement "+
"preference: %v", err)
}
// We create the state-machine object which wraps the database state. // We create the state-machine object which wraps the database state.
lnChannel, err := lnwallet.NewLightningChannel(nil, nil, f.cfg.FeeEstimator, lnChannel, err := lnwallet.NewLightningChannel(nil, nil, f.cfg.FeeEstimator,
completeChan) completeChan)
@ -1715,23 +1641,14 @@ func (f *fundingManager) handleFundingConfirmation(completeChan *channeldb.OpenC
if err != nil { if err != nil {
return fmt.Errorf("failed sending fundingLocked: %v", err) return fmt.Errorf("failed sending fundingLocked: %v", err)
} }
err = f.addToRouterGraph(completeChan, lnChannel, shortChanID, private) err = f.addToRouterGraph(completeChan, shortChanID)
if err != nil { if err != nil {
return fmt.Errorf("failed adding to router graph: %v", err) return fmt.Errorf("failed adding to router graph: %v", err)
} }
err = f.annAfterSixConfs(completeChan, shortChanID)
if private { if err != nil {
// We delete the channel from our internal database. return fmt.Errorf("failed sending channel announcement: %v",
err := f.deleteChannelOpeningState(&completeChan.FundingOutpoint) err)
if err != nil {
return fmt.Errorf("error deleting channel state: %v", err)
}
} else {
err = f.annAfterSixConfs(completeChan, lnChannel, shortChanID)
if err != nil {
return fmt.Errorf("failed sending channel announcement: %v",
err)
}
} }
return nil return nil
@ -1813,19 +1730,26 @@ func (f *fundingManager) sendFundingLocked(completeChan *channeldb.OpenChannel,
return nil return nil
} }
// addToRouterGraph sends a private ChannelAnnouncement and a private // addToRouterGraph sends a ChannelAnnouncement and a ChannelUpdate to the
// ChannelUpdate to the gossiper so that it is added to the Router's internal // gossiper so that the channel is added to the Router's internal graph.
// graph before the announcement_signatures is sent in // These announcement messages are NOT broadcasted to the greater network,
// annAfterSixConfs. These private announcement messages are NOT // only to the channel counter party. The proofs required to announce the
// broadcasted to the greater network. // channel to the greater network will be created and sent in annAfterSixConfs.
func (f *fundingManager) addToRouterGraph(completeChan *channeldb.OpenChannel, func (f *fundingManager) addToRouterGraph(completeChan *channeldb.OpenChannel,
shortChanID *lnwire.ShortChannelID, private bool) error { shortChanID *lnwire.ShortChannelID) error {
chanID := lnwire.NewChanIDFromOutPoint(&completeChan.FundingOutpoint) chanID := lnwire.NewChanIDFromOutPoint(&completeChan.FundingOutpoint)
// We'll obtain their min HTLC as we'll use this value within our
// ChannelUpdate. We use this value isn't of ours, as the remote party
// will be the one that's carrying the HTLC towards us.
remoteMinHTLC := completeChan.RemoteChanCfg.MinHTLC
ann, err := f.newChanAnnouncement(f.cfg.IDKey, completeChan.IdentityPub, ann, err := f.newChanAnnouncement(f.cfg.IDKey, completeChan.IdentityPub,
completeChan.LocalChanCfg.MultiSigKey, completeChan.LocalChanCfg.MultiSigKey,
completeChan.RemoteChanCfg.MultiSigKey, *shortChanID, chanID) completeChan.RemoteChanCfg.MultiSigKey, *shortChanID, chanID,
remoteMinHTLC,
)
if err != nil { if err != nil {
return fmt.Errorf("error generating channel "+ return fmt.Errorf("error generating channel "+
"announcement: %v", err) "announcement: %v", err)
@ -1833,21 +1757,30 @@ func (f *fundingManager) addToRouterGraph(completeChan *channeldb.OpenChannel,
// Send ChannelAnnouncement and ChannelUpdate to the gossiper to add // Send ChannelAnnouncement and ChannelUpdate to the gossiper to add
// to the Router's topology. // to the Router's topology.
if err = f.cfg.SendLocalAnnouncement(ann.chanAnn); err != nil { if err = f.cfg.SendAnnouncement(ann.chanAnn); err != nil {
return fmt.Errorf("error sending private channel "+ if routing.IsError(err, routing.ErrOutdated, routing.ErrIgnored) {
"announcement: %v", err) fndgLog.Debugf("Router rejected ChannelAnnouncement: %v",
err)
} else {
return fmt.Errorf("error sending channel "+
"announcement: %v", err)
}
} }
if err = f.cfg.SendLocalAnnouncement(ann.chanUpdateAnn); err != nil { if err = f.cfg.SendAnnouncement(ann.chanUpdateAnn); err != nil {
return fmt.Errorf("error sending private channel "+ if routing.IsError(err, routing.ErrOutdated, routing.ErrIgnored) {
"update: %v", err) fndgLog.Debugf("Router rejected ChannelUpdate: %v", err)
} else {
return fmt.Errorf("error sending channel "+
"update: %v", err)
}
} }
// As the channel is now added to the ChannelRouter's topology, the // As the channel is now added to the ChannelRouter's topology, the
// channel is moved to the next state of the state machine. It will be // channel is moved to the next state of the state machine. It will be
// moved to the last state (actually deleted from the database) after // moved to the last state (actually deleted from the database) after
// the channel is finally announced. // the channel is finally announced.
err = f.saveChannelOpeningState(&completeChan.FundingOutpoint, addedToRouterGraph, err = f.saveChannelOpeningState(&completeChan.FundingOutpoint,
shortChanID) addedToRouterGraph, shortChanID)
if err != nil { if err != nil {
return fmt.Errorf("error setting channel state to"+ return fmt.Errorf("error setting channel state to"+
" addedToRouterGraph: %v", err) " addedToRouterGraph: %v", err)
@ -1859,61 +1792,86 @@ func (f *fundingManager) addToRouterGraph(completeChan *channeldb.OpenChannel,
// annAfterSixConfs broadcasts the necessary channel announcement messages to // annAfterSixConfs broadcasts the necessary channel announcement messages to
// the network after 6 confs. Should be called after the fundingLocked message // the network after 6 confs. Should be called after the fundingLocked message
// is sent and the channel is added to the router graph (channelState is // is sent and the channel is added to the router graph (channelState is
// 'addedToRouterGraph') and the channel is ready to be used. // 'addedToRouterGraph') and the channel is ready to be used. This is the last
// step in the channel opening process, and the opening state will be deleted
// from the database if successful.
func (f *fundingManager) annAfterSixConfs(completeChan *channeldb.OpenChannel, func (f *fundingManager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
shortChanID *lnwire.ShortChannelID) error { shortChanID *lnwire.ShortChannelID) error {
// Register with the ChainNotifier for a notification once the // If this channel is meant to be announced to the greater network,
// funding transaction reaches 6 confirmations. // wait until the funding tx has reached 6 confirmations before
txid := completeChan.FundingOutpoint.Hash // announcing it.
confNtfn, err := f.cfg.Notifier.RegisterConfirmationsNtfn(&txid, 6, announceChan := completeChan.ChannelFlags&lnwire.FFAnnounceChannel != 0
completeChan.FundingBroadcastHeight) if !announceChan {
if err != nil { fndgLog.Debugf("Will not announce private channel %v.",
return fmt.Errorf("Unable to register for confirmation of "+ shortChanID.ToUint64())
"ChannelPoint(%v): %v", completeChan.FundingOutpoint, err) } else {
} // Register with the ChainNotifier for a notification once the
// funding transaction reaches at least 6 confirmations.
// Wait until 6 confirmations has been reached or the wallet signals numConfs := uint32(completeChan.NumConfsRequired)
// a shutdown. if numConfs < 6 {
select { numConfs = 6
case _, ok := <-confNtfn.Confirmed:
if !ok {
return fmt.Errorf("ChainNotifier shutting down, cannot "+
"complete funding flow for ChannelPoint(%v)",
completeChan.FundingOutpoint)
} }
case <-f.quit: txid := completeChan.FundingOutpoint.Hash
return fmt.Errorf("fundingManager shutting down, stopping funding "+ fndgLog.Debugf("Will announce channel %v after ChannelPoint"+
"flow for ChannelPoint(%v)", completeChan.FundingOutpoint) "(%v) has gotten %d confirmations",
shortChanID.ToUint64(), completeChan.FundingOutpoint,
numConfs)
confNtfn, err := f.cfg.Notifier.RegisterConfirmationsNtfn(&txid,
numConfs, completeChan.FundingBroadcastHeight)
if err != nil {
return fmt.Errorf("Unable to register for confirmation of "+
"ChannelPoint(%v): %v", completeChan.FundingOutpoint, err)
}
// Wait until 6 confirmations has been reached or the wallet signals
// a shutdown.
select {
case _, ok := <-confNtfn.Confirmed:
if !ok {
return fmt.Errorf("ChainNotifier shutting down, cannot "+
"complete funding flow for ChannelPoint(%v)",
completeChan.FundingOutpoint)
}
// Fallthrough.
case <-f.quit:
return fmt.Errorf("fundingManager shutting down, stopping funding "+
"flow for ChannelPoint(%v)", completeChan.FundingOutpoint)
}
fundingPoint := completeChan.FundingOutpoint
chanID := lnwire.NewChanIDFromOutPoint(&fundingPoint)
fndgLog.Infof("Announcing ChannelPoint(%v), short_chan_id=%v",
&fundingPoint, spew.Sdump(shortChanID))
// We'll obtain their min HTLC as we'll use this value within our
// ChannelUpdate. We use this value isn't of ours, as the remote party
// will be the one that's carrying the HTLC towards us.
remoteMinHTLC := completeChan.RemoteChanCfg.MinHTLC
// Create and broadcast the proofs required to make this channel
// public and usable for other nodes for routing.
err = f.announceChannel(f.cfg.IDKey, completeChan.IdentityPub,
completeChan.LocalChanCfg.MultiSigKey,
completeChan.RemoteChanCfg.MultiSigKey, *shortChanID, chanID,
remoteMinHTLC,
)
if err != nil {
return fmt.Errorf("channel announcement failed: %v", err)
}
fndgLog.Debugf("Channel with ChannelPoint(%v), short_chan_id=%v "+
"announced", &fundingPoint, spew.Sdump(shortChanID))
} }
fundingPoint := completeChan.FundingOutpoint // We delete the channel opening state from our internal database
chanID := lnwire.NewChanIDFromOutPoint(&fundingPoint) // as the opening process has succeeded. We can do this because we
// assume the AuthenticatedGossiper queues the announcement messages,
fndgLog.Infof("Announcing ChannelPoint(%v), short_chan_id=%v", // and persists them in case of a daemon shutdown.
&fundingPoint, spew.Sdump(shortChanID)) err := f.deleteChannelOpeningState(&completeChan.FundingOutpoint)
// We'll obtain their min HTLC as we'll use this value within our
// ChannelUpdate. We use this value isn't of ours, as the remote party
// will be the one that's carrying the HTLC towards us.
remoteMinHTLC := completeChan.RemoteChanCfg.MinHTLC
// Register the new link with the L3 routing manager so this new
// channel can be utilized during path finding.
err := f.announceChannel(f.cfg.IDKey, completeChan.IdentityPub,
completeChan.LocalChanCfg.MultiSigKey,
completeChan.RemoteChanCfg.MultiSigKey, *shortChanID, chanID,
remoteMinHTLC,
)
if err != nil {
return fmt.Errorf("channel announcement failed: %v", err)
}
// After the channel is successfully announced from the fundingManager,
// we delete the channel from our internal database. We can do this
// because we assume the AuthenticatedGossiper queues the announcement
// messages, and persists them in case of a daemon shutdown.
err = f.deleteChannelOpeningState(&fundingPoint)
if err != nil { if err != nil {
return fmt.Errorf("error deleting channel state: %v", err) return fmt.Errorf("error deleting channel state: %v", err)
} }
@ -2240,8 +2198,13 @@ func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKe
// the ChannelUpdate announcement messages. The channel proof and node // the ChannelUpdate announcement messages. The channel proof and node
// announcements are broadcast to the greater network. // announcements are broadcast to the greater network.
if err = f.cfg.SendAnnouncement(ann.chanProof); err != nil { if err = f.cfg.SendAnnouncement(ann.chanProof); err != nil {
fndgLog.Errorf("Unable to send channel proof: %v", err) if routing.IsError(err, routing.ErrOutdated, routing.ErrIgnored) {
return err fndgLog.Debugf("Router rejected AnnounceSignatures: %v",
err)
} else {
fndgLog.Errorf("Unable to send channel proof: %v", err)
return err
}
} }
// Now that the channel is announced to the network, we will also // Now that the channel is announced to the network, we will also
@ -2254,9 +2217,14 @@ func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKe
return err return err
} }
if err = f.cfg.SendAnnouncement(&nodeAnn); err != nil { if err := f.cfg.SendAnnouncement(&nodeAnn); err != nil {
fndgLog.Errorf("Unable to send node announcement: %v", err) if routing.IsError(err, routing.ErrOutdated, routing.ErrIgnored) {
return err fndgLog.Debugf("Router rejected NodeAnnouncement: %v",
err)
} else {
fndgLog.Errorf("Unable to send node announcement: %v", err)
return err
}
} }
return nil return nil
} }
@ -2305,12 +2273,20 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
// multiply the computed sat/weight by 1000 to arrive at fee-per-kw. // multiply the computed sat/weight by 1000 to arrive at fee-per-kw.
commitFeePerKw := feePerWeight * 1000 commitFeePerKw := feePerWeight * 1000
// We set the channel flags to indicate whether we want this channel
// to be announced to the network.
var channelFlags lnwire.FundingFlag
if !msg.openChanReq.private {
// This channel will be announced.
channelFlags = lnwire.FFAnnounceChannel
}
// Initialize a funding reservation with the local wallet. If the // Initialize a funding reservation with the local wallet. If the
// wallet doesn't have enough funds to commit to this channel, then the // wallet doesn't have enough funds to commit to this channel, then the
// request will fail, and be aborted. // request will fail, and be aborted.
reservation, err := f.cfg.Wallet.InitChannelReservation(capacity, reservation, err := f.cfg.Wallet.InitChannelReservation(capacity,
localAmt, msg.pushAmt, commitFeePerKw, msg.fundingFeePerWeight, localAmt, msg.pushAmt, commitFeePerKw, msg.fundingFeePerWeight,
peerKey, msg.peerAddress.Address, &msg.chainHash) peerKey, msg.peerAddress.Address, &msg.chainHash, channelFlags)
if err != nil { if err != nil {
msg.err <- err msg.err <- err
return return
@ -2358,18 +2334,6 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
fndgLog.Infof("Starting funding workflow with %v for pendingID(%x)", fndgLog.Infof("Starting funding workflow with %v for pendingID(%x)",
msg.peerAddress.Address, chanID) msg.peerAddress.Address, chanID)
// Save the announcement preference of this pending channel
var channelFlags byte
if msg.openChanReq.private {
// This channel will be private
channelFlags = 1
f.pendingChanAnnPrefs[chanID] = true
} else {
// This channel will be publicly announced to the greater network.
channelFlags = 0
f.pendingChanAnnPrefs[chanID] = false
}
fundingOpen := lnwire.OpenChannel{ fundingOpen := lnwire.OpenChannel{
ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash, ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash,
PendingChannelID: chanID, PendingChannelID: chanID,
@ -2388,7 +2352,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
HtlcPoint: ourContribution.HtlcBasePoint, HtlcPoint: ourContribution.HtlcBasePoint,
DelayedPaymentPoint: ourContribution.DelayBasePoint, DelayedPaymentPoint: ourContribution.DelayBasePoint,
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint, FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
ChannelFlags: lnwire.FFAnnounceChannel, ChannelFlags: channelFlags,
} }
if err := f.cfg.SendToPeer(peerKey, &fundingOpen); err != nil { if err := f.cfg.SendToPeer(peerKey, &fundingOpen); err != nil {
fndgLog.Errorf("Unable to send funding request message: %v", err) fndgLog.Errorf("Unable to send funding request message: %v", err)
@ -2470,7 +2434,6 @@ func (f *fundingManager) handleErrorMsg(fmsg *fundingErrorMsg) {
) )
} }
delete(f.pendingChanAnnPrefs, chanID)
if _, err := f.cancelReservationCtx(peerKey, chanID); err != nil { if _, err := f.cancelReservationCtx(peerKey, chanID); err != nil {
fndgLog.Warnf("unable to delete reservation: %v", err) fndgLog.Warnf("unable to delete reservation: %v", err)
return return
@ -2539,86 +2502,6 @@ func copyPubKey(pub *btcec.PublicKey) *btcec.PublicKey {
} }
} }
// saveOpenChanAnnPref saves an open channel's announcement preference.
func (f *fundingManager) saveOpenChanAnnPref(chanPoint *wire.OutPoint,
pref bool) error {
return f.cfg.Wallet.Cfg.Database.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(openChanAnnPrefBucket)
if err != nil {
return err
}
var outpointBytes bytes.Buffer
if err = writeOutpoint(&outpointBytes, chanPoint); err != nil {
return err
}
scratch := make([]byte, 0)
var b byte
if pref {
b = 1
} else {
b = 0
}
scratch = append(scratch[:], b)
return bucket.Put(outpointBytes.Bytes(), scratch[:])
})
}
// getOpenChanAnnPref retrives an open channel's announcement preference.
func (f *fundingManager) getOpenChanAnnPref(chanPoint *wire.OutPoint) (bool, error) {
var pref bool
err := f.cfg.Wallet.Cfg.Database.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(openChanAnnPrefBucket)
if bucket == nil {
return fmt.Errorf("Channel announcement preference " +
"not found")
}
var outpointBytes bytes.Buffer
if err := writeOutpoint(&outpointBytes, chanPoint); err != nil {
return err
}
value := bucket.Get(outpointBytes.Bytes())
if value == nil {
return fmt.Errorf("Channel announcement preference " +
"not found")
}
if value[0] == 1 {
pref = true
}
return nil
})
if err != nil {
return false, err
}
return pref, nil
}
// deleteOpenChanAnnPref deletes an open channel's announcement preference.
func (f *fundingManager) deleteOpenChanAnnPref(chanPoint *wire.OutPoint) error {
return f.cfg.Wallet.Cfg.Database.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(openChanAnnPrefBucket)
if bucket == nil {
return fmt.Errorf("Bucket not found")
}
var outpointBytes bytes.Buffer
if err := writeOutpoint(&outpointBytes, chanPoint); err != nil {
return err
}
return bucket.Delete(outpointBytes.Bytes())
})
}
// saveChannelOpeningState saves the channelOpeningState for the provided // saveChannelOpeningState saves the channelOpeningState for the provided
// chanPoint to the channelOpeningStateBucket. // chanPoint to the channelOpeningStateBucket.
func (f *fundingManager) saveChannelOpeningState(chanPoint *wire.OutPoint, func (f *fundingManager) saveChannelOpeningState(chanPoint *wire.OutPoint,