funding: we process the funding locked message _after_ shortChanID is known

This commit adds a new bit of synchronization to the funding manager to
ensure that we only process the funding locked message directly _after_
the short channel ID has become available within the channel. This
fixes a possible bug wherein we would receive the funding locked
message, and register the channel with the switch without yet knowing
the short chan ID. This would then cause any HTLC’s routed to the new
channel to fail, as it would be using the incorrect short channel ID.
This commit is contained in:
Olaoluwa Osuntokun 2017-06-16 23:19:39 +02:00
parent 4173b9748d
commit 26c14c7de5
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2

@ -225,6 +225,9 @@ type fundingManager struct {
barrierMtx sync.RWMutex
newChanBarriers map[lnwire.ChannelID]chan struct{}
localDiscoveryMtx sync.Mutex
localDiscoverySignals map[lnwire.ChannelID]chan struct{}
quit chan struct{}
wg sync.WaitGroup
}
@ -233,14 +236,15 @@ type fundingManager struct {
// fundingManager.
func newFundingManager(cfg fundingConfig) (*fundingManager, error) {
return &fundingManager{
cfg: &cfg,
chanIDKey: cfg.TempChanIDSeed,
activeReservations: make(map[serializedPubKey]pendingChannels),
newChanBarriers: make(map[lnwire.ChannelID]chan struct{}),
fundingMsgs: make(chan interface{}, msgBufferSize),
fundingRequests: make(chan *initFundingMsg, msgBufferSize),
queries: make(chan interface{}, 1),
quit: make(chan struct{}),
cfg: &cfg,
chanIDKey: cfg.TempChanIDSeed,
activeReservations: make(map[serializedPubKey]pendingChannels),
newChanBarriers: make(map[lnwire.ChannelID]chan struct{}),
fundingMsgs: make(chan interface{}, msgBufferSize),
fundingRequests: make(chan *initFundingMsg, msgBufferSize),
localDiscoverySignals: make(map[lnwire.ChannelID]chan struct{}),
queries: make(chan interface{}, 1),
quit: make(chan struct{}),
}, nil
}
@ -276,6 +280,8 @@ func (f *fundingManager) Start() error {
f.newChanBarriers[chanID] = make(chan struct{})
f.barrierMtx.Unlock()
f.localDiscoverySignals[chanID] = make(chan struct{})
doneChan := make(chan struct{})
go f.waitForFundingConfirmation(channel, doneChan)
}
@ -391,7 +397,7 @@ func (f *fundingManager) reservationCoordinator() {
case *fundingSignCompleteMsg:
f.handleFundingSignComplete(fmsg)
case *fundingLockedMsg:
f.handleFundingLocked(fmsg)
go f.handleFundingLocked(fmsg)
case *fundingErrorMsg:
f.handleErrorMsg(fmsg)
}
@ -797,6 +803,13 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
return
}
// Create an entry in the local discovery map so we can ensure that we
// process the channel confirmation fully before we receive a funding
// locked message.
f.localDiscoveryMtx.Lock()
f.localDiscoverySignals[channelID] = make(chan struct{})
f.localDiscoveryMtx.Unlock()
go func() {
doneChan := make(chan struct{})
go f.waitForFundingConfirmation(completeChan, doneChan)
@ -829,6 +842,15 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
return
}
// Create an entry in the local discovery map so we can ensure that we
// process the channel confirmation fully before we receive a funding
// locked message.
fundingPoint := resCtx.reservation.FundingOutpoint()
permChanID := lnwire.NewChanIDFromOutPoint(fundingPoint)
f.localDiscoveryMtx.Lock()
f.localDiscoverySignals[permChanID] = make(chan struct{})
f.localDiscoveryMtx.Unlock()
// The remote peer has responded with a signature for our commitment
// transaction. We'll verify the signature for validity, then commit
// the state to disk as we can now open the channel.
@ -978,6 +1000,14 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open
go f.announceChannel(f.cfg.IDKey, completeChan.IdentityPub,
channel.LocalFundingKey, channel.RemoteFundingKey,
shortChanID, chanID)
// Finally, as the local channel discovery has been fully processed,
// we'll trigger the signal indicating that it's safe foe any funding
// locked messages related to this channel to be processed.
f.localDiscoveryMtx.Lock()
close(f.localDiscoverySignals[chanID])
f.localDiscoveryMtx.Unlock()
return
}
@ -992,6 +1022,22 @@ func (f *fundingManager) processFundingLocked(msg *lnwire.FundingLocked,
// handleFundingLocked finalizes the channel funding process and enables the channel
// to enter normal operating mode.
func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
f.localDiscoveryMtx.Lock()
localDiscoverySignal := f.localDiscoverySignals[fmsg.msg.ChanID]
f.localDiscoveryMtx.Unlock()
// Before we proceed with processing the funding locked message, we'll
// wait for the lcoal waitForFundingConfirmation goroutine to signal
// that it has the necessary state in place. Otherwise, we may be
// missing critical information required to handle forwarded HTLC's.
<-localDiscoverySignal
// With the signal received, we can now safely delete the entry from
// the map.
f.localDiscoveryMtx.Lock()
delete(f.localDiscoverySignals, fmsg.msg.ChanID)
f.localDiscoveryMtx.Unlock()
// First, we'll attempt to locate the channel who's funding workflow is
// being finalized by this message. We got to the database rather than
// our reservation map as we may have restarted, mid funding flow.