fundingmanager: move final funding steps from wallet to funding manager.

Once a channel funding process has advanced to the point of broadcasting
the funding transaction, the state of the channel should be persisted
so that the nodes can disconnect or go down without having to wait for the
funding transaction to be confirmed on the blockchain.

Previously, the finalization of the funding process was handled by a
combination of the funding manager, the peer and the wallet, but if
the remote peer is no longer online or no longer connected, this flow
will no longer work. This commit moves all funding steps following
the transaction broadcast into the funding manager, which is available
as long as the daemon is running.
This commit is contained in:
bryanvu authored and Olaoluwa Osuntokun committed 2017-02-24 11:37:33 -08:00
1 parent 508138f8c8
commit e549a3f0ed
10 files changed
+402 -512

No files matched your search

+10 -131
View File
@@ -1,7 +1,6 @@
package lnwallet
import (
"errors"
"fmt"
"net"
"sync"
@@ -185,6 +184,10 @@ type addCounterPartySigsMsg struct {
// version of the commitment transaction.
theirCommitmentSig []byte
// This channel is used to return the completed channel after the wallet
// has completed all of its stages in the funding process.
completeChan chan *channeldb.OpenChannel
// NOTE: In order to avoid deadlocks, this channel MUST be buffered.
err chan error
}
@@ -215,19 +218,9 @@ type addSingleFunderSigsMsg struct {
// the commitment transaction.
obsfucator [StateHintSize]byte
// NOTE: In order to avoid deadlocks, this channel MUST be buffered.
err chan error
}
// channelOpenMsg is the final message sent to finalize a single funder channel
// workflow to which we are the responder to. This message is sent once the
// remote peer deems the channel open, meaning it has reached a sufficient
// number of confirmations in the blockchain.
type channelOpenMsg struct {
pendingFundingID uint64
// TODO(roasbeef): move verification up to upper layer, yeh?
spvProof []byte
// This channel is used to return the completed channel after the wallet
// has completed all of its stages in the funding process.
completeChan chan *channeldb.OpenChannel
// NOTE: In order to avoid deadlocks, this channel MUST be buffered.
err chan error
@@ -461,8 +454,6 @@ out:
l.handleSingleFunderSigs(msg)
case *addCounterPartySigsMsg:
l.handleFundingCounterPartySigs(msg)
case *channelOpenMsg:
l.handleChannelOpen(msg)
}
case <-l.quit:
// TODO: do some clean up
@@ -1072,10 +1063,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
return
}
// Create a goroutine to watch the chain so we can open the channel once
// the funding tx has enough confirmations.
go l.openChannelAfterConfirmations(res)
msg.completeChan <- res.partialState
msg.err <- nil
}
@@ -1192,124 +1180,15 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
}
pendingReservation.ourCommitmentSig = sigTheirCommit
req.err <- nil
}
// handleChannelOpen completes a single funder reservation to which we are the
// responder. This method saves the channel state to disk, finally "opening"
// the channel by sending it over to the caller of the reservation via the
// channel dispatch channel.
func (l *LightningWallet) handleChannelOpen(req *channelOpenMsg) {
l.limboMtx.RLock()
res, ok := l.fundingLimbo[req.pendingFundingID]
l.limboMtx.RUnlock()
if !ok {
req.err <- fmt.Errorf("attempted to update non-existant " +
"funding state")
res.chanOpen <- nil
return
}
// Grab the mutex on the ChannelReservation to ensure thead-safety
res.Lock()
defer res.Unlock()
// Funding complete, this entry can be removed from limbo.
l.limboMtx.Lock()
delete(l.fundingLimbo, res.reservationID)
l.limboMtx.Unlock()
// Add the complete funding transaction to the DB, in it's open bucket
// which will be used for the lifetime of this channel.
if err := res.partialState.SyncPending(res.nodeAddr); err != nil {
if err := pendingReservation.partialState.SyncPending(pendingReservation.nodeAddr); err != nil {
req.err <- err
res.chanOpen <- nil
return
}
// Finally, create and officially open the payment channel!
// TODO(roasbeef): CreationTime once tx is 'open'
channel, err := NewLightningChannel(l.Signer, l.chainNotifier,
res.partialState)
if err != nil {
req.err <- err
res.chanOpen <- nil
return
}
req.completeChan <- pendingReservation.partialState
req.err <- nil
res.chanOpen <- &openChanDetails{
channel: channel,
}
// As this reservation has concluded, we can now safely remove the
// reservation from the limbo map.
l.limboMtx.Lock()
delete(l.fundingLimbo, req.pendingFundingID)
l.limboMtx.Unlock()
}
// openChannelAfterConfirmations creates, and opens a payment channel after
// the funding transaction created within the passed channel reservation
// obtains the specified number of confirmations.
func (l *LightningWallet) openChannelAfterConfirmations(res *ChannelReservation) {
// Register with the ChainNotifier for a notification once the funding
// transaction reaches `numConfs` confirmations.
txid := res.fundingTx.TxHash()
numConfs := uint32(res.numConfsToOpen)
confNtfn, _ := l.chainNotifier.RegisterConfirmationsNtfn(&txid, numConfs)
walletLog.Infof("Waiting for funding tx (txid: %v) to reach %v confirmations",
txid, numConfs)
// Wait until the specified number of confirmations has been reached,
// or the wallet signals a shutdown.
var (
confDetails *chainntnfs.TxConfirmation
ok bool
)
out:
select {
case confDetails, ok = <-confNtfn.Confirmed:
// Reading a falsey value for the second parameter indicates that
// the notifier is in the process of shutting down. Therefore, we
// don't count this as the signal that the funding transaction has
// been confirmed.
if !ok {
res.chanOpenErr <- errors.New("wallet shutting down")
res.chanOpen <- nil
return
}
break out
case <-l.quit:
res.chanOpenErr <- errors.New("wallet shutting down")
res.chanOpen <- nil
return
}
// Finally, create and officially open the payment channel!
// TODO(roasbeef): CreationTime once tx is 'open'
channel, err := NewLightningChannel(l.Signer, l.chainNotifier,
res.partialState)
if err != nil {
res.chanOpenErr <- err
res.chanOpen <- nil
return
}
res.chanOpenErr <- nil
res.chanOpen <- &openChanDetails{
channel: channel,
blockHeight: confDetails.BlockHeight,
txIndex: confDetails.TxIndex,
}
// As this reservation has concluded, we can now safely remove the
// reservation from the limbo map.
l.limboMtx.Lock()
delete(l.fundingLimbo, res.reservationID)
l.limboMtx.Unlock()
}
// selectCoinsAndChange performs coin selection in order to obtain witness