mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
lnwallet+funding: properly propagate NewLightningChannel errors
This commit ensures that we now properly handle and propagate errors that arise when attempting to create a new channel after the funding transaction is believed to be confirmed. A previous edge case would arise when a user attempted to create a new channel, but their corresponding btcd node wasn’t yet fully synced.
This commit is contained in:
parent
db40b4322e
commit
de70175be6
@ -726,7 +726,13 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
|
|||||||
go func() {
|
go func() {
|
||||||
// TODO(roasbeef): need to persist pending broadcast channels,
|
// TODO(roasbeef): need to persist pending broadcast channels,
|
||||||
// send chan open proof during scan of blocks mined while down.
|
// send chan open proof during scan of blocks mined while down.
|
||||||
openChan, confHeight, confBlockIndex := resCtx.reservation.DispatchChan()
|
openChanDetails, err := resCtx.reservation.DispatchChan()
|
||||||
|
if err != nil {
|
||||||
|
fndgLog.Errorf("Unable to dispatch "+
|
||||||
|
"ChannelPoint(%v): %v", fundingPoint, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// This reservation is no longer pending as the funding
|
// This reservation is no longer pending as the funding
|
||||||
// transaction has been fully confirmed.
|
// transaction has been fully confirmed.
|
||||||
f.deleteReservationCtx(peerID, chanID)
|
f.deleteReservationCtx(peerID, chanID)
|
||||||
@ -739,19 +745,19 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
|
|||||||
|
|
||||||
// First we send the newly opened channel to the source server
|
// First we send the newly opened channel to the source server
|
||||||
// peer.
|
// peer.
|
||||||
fmsg.peer.newChannels <- openChan
|
fmsg.peer.newChannels <- openChanDetails.Channel
|
||||||
|
|
||||||
// Afterwards we send the breach arbiter the new channel so it
|
// Afterwards we send the breach arbiter the new channel so it
|
||||||
// can watch for attempts to breach the channel's contract by
|
// can watch for attempts to breach the channel's contract by
|
||||||
// the remote party.
|
// the remote party.
|
||||||
f.breachAribter.newContracts <- openChan
|
f.breachAribter.newContracts <- openChanDetails.Channel
|
||||||
|
|
||||||
// With the block height and the transaction index known, we
|
// With the block height and the transaction index known, we
|
||||||
// can construct the compact chainID which is used on the
|
// can construct the compact chainID which is used on the
|
||||||
// network to unique identify channels.
|
// network to unique identify channels.
|
||||||
chainID := lnwire.ChannelID{
|
chainID := lnwire.ChannelID{
|
||||||
BlockHeight: confHeight,
|
BlockHeight: openChanDetails.ConfirmationHeight,
|
||||||
TxIndex: confBlockIndex,
|
TxIndex: openChanDetails.TransactionIndex,
|
||||||
TxPosition: uint16(fundingPoint.Index),
|
TxPosition: uint16(fundingPoint.Index),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,8 +773,8 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
|
|||||||
// TODO(roasbeef): should include sigs from funding
|
// TODO(roasbeef): should include sigs from funding
|
||||||
// locked
|
// locked
|
||||||
// * should be moved to after funding locked is recv'd
|
// * should be moved to after funding locked is recv'd
|
||||||
f.announceChannel(fmsg.peer.server, openChan, chainID, f.fakeProof,
|
f.announceChannel(fmsg.peer.server, openChanDetails.Channel,
|
||||||
f.fakeProof)
|
chainID, f.fakeProof, f.fakeProof)
|
||||||
|
|
||||||
// Finally give the caller a final update notifying them that
|
// Finally give the caller a final update notifying them that
|
||||||
// the channel is now open.
|
// the channel is now open.
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -479,8 +480,12 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, wallet *lnwallet
|
|||||||
// Assert that the channel opens after a single block.
|
// Assert that the channel opens after a single block.
|
||||||
lnChan := make(chan *lnwallet.LightningChannel, 1)
|
lnChan := make(chan *lnwallet.LightningChannel, 1)
|
||||||
go func() {
|
go func() {
|
||||||
channel, _, _ := chanReservation.DispatchChan()
|
openDetails, err := chanReservation.DispatchChan()
|
||||||
lnChan <- channel
|
if err != nil {
|
||||||
|
t.Fatalf("unable to finalize reservation: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lnChan <- openDetails.Channel
|
||||||
}()
|
}()
|
||||||
lnc := assertChannelOpen(t, miner, uint32(numReqConfs), lnChan)
|
lnc := assertChannelOpen(t, miner, uint32(numReqConfs), lnChan)
|
||||||
|
|
||||||
@ -757,8 +762,12 @@ func testSingleFunderReservationWorkflowInitiator(miner *rpctest.Harness,
|
|||||||
|
|
||||||
lnChan := make(chan *lnwallet.LightningChannel, 1)
|
lnChan := make(chan *lnwallet.LightningChannel, 1)
|
||||||
go func() {
|
go func() {
|
||||||
channel, _, _ := chanReservation.DispatchChan()
|
openDetails, err := chanReservation.DispatchChan()
|
||||||
lnChan <- channel
|
if err != nil {
|
||||||
|
t.Fatalf("unable to open channel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lnChan <- openDetails.Channel
|
||||||
}()
|
}()
|
||||||
assertChannelOpen(t, miner, uint32(numReqConfs), lnChan)
|
assertChannelOpen(t, miner, uint32(numReqConfs), lnChan)
|
||||||
}
|
}
|
||||||
@ -915,7 +924,8 @@ func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness,
|
|||||||
// Some period of time later, Bob presents us with an SPV proof
|
// Some period of time later, Bob presents us with an SPV proof
|
||||||
// attesting to an open channel. At this point Alice recognizes the
|
// attesting to an open channel. At this point Alice recognizes the
|
||||||
// channel, saves the state to disk, and creates the channel itself.
|
// channel, saves the state to disk, and creates the channel itself.
|
||||||
if _, err := chanReservation.FinalizeReservation(); err != nil {
|
_, err = chanReservation.FinalizeReservation()
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "No information") {
|
||||||
t.Fatalf("unable to finalize reservation: %v", err)
|
t.Fatalf("unable to finalize reservation: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,8 @@ type ChannelReservation struct {
|
|||||||
// confirmation details will be sent on once the channel is considered
|
// confirmation details will be sent on once the channel is considered
|
||||||
// 'open'. A channel is open once the funding transaction has reached a
|
// 'open'. A channel is open once the funding transaction has reached a
|
||||||
// sufficient number of confirmations.
|
// sufficient number of confirmations.
|
||||||
chanOpen chan *openChanDetails
|
chanOpen chan *openChanDetails
|
||||||
|
chanOpenErr chan error
|
||||||
|
|
||||||
wallet *LightningWallet
|
wallet *LightningWallet
|
||||||
}
|
}
|
||||||
@ -213,6 +214,7 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
|||||||
pushSat: pushSat,
|
pushSat: pushSat,
|
||||||
reservationID: id,
|
reservationID: id,
|
||||||
chanOpen: make(chan *openChanDetails, 1),
|
chanOpen: make(chan *openChanDetails, 1),
|
||||||
|
chanOpenErr: make(chan error, 1),
|
||||||
wallet: wallet,
|
wallet: wallet,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -434,6 +436,23 @@ func (r *ChannelReservation) Cancel() error {
|
|||||||
return <-errChan
|
return <-errChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenChannelDetails wraps the finalized fully confirmed channel which
|
||||||
|
// resulted from a ChannelReservation instance with details concerning exactly
|
||||||
|
// _where_ in the chain the channel was ultimately opened.
|
||||||
|
type OpenChannelDetails struct {
|
||||||
|
// Channel is the active channel created by an instance of a
|
||||||
|
// ChannelReservation and the required funding workflow.
|
||||||
|
Channel *LightningChannel
|
||||||
|
|
||||||
|
// ConfirmationHeight is the block height within the chain that included
|
||||||
|
// the channel.
|
||||||
|
ConfirmationHeight uint32
|
||||||
|
|
||||||
|
// TransactionIndex is the index within the confirming block that the
|
||||||
|
// transaction resides.
|
||||||
|
TransactionIndex uint32
|
||||||
|
}
|
||||||
|
|
||||||
// DispatchChan returns a channel which will be sent on once the funding
|
// DispatchChan returns a channel which will be sent on once the funding
|
||||||
// transaction for this pending payment channel obtains the configured number
|
// transaction for this pending payment channel obtains the configured number
|
||||||
// of confirmations. Once confirmations have been obtained, a fully initialized
|
// of confirmations. Once confirmations have been obtained, a fully initialized
|
||||||
@ -441,12 +460,17 @@ func (r *ChannelReservation) Cancel() error {
|
|||||||
//
|
//
|
||||||
// NOTE: If this method is called before .CompleteReservation(), it will block
|
// NOTE: If this method is called before .CompleteReservation(), it will block
|
||||||
// indefinitely.
|
// indefinitely.
|
||||||
func (r *ChannelReservation) DispatchChan() (*LightningChannel, uint32, uint32) {
|
func (r *ChannelReservation) DispatchChan() (*OpenChannelDetails, error) {
|
||||||
// TODO(roasbeef): goroutine sending in wallet should be lifted up into
|
if err := <-r.chanOpenErr; err != nil {
|
||||||
// the fundingMgr
|
return nil, err
|
||||||
openDetails := <-r.chanOpen
|
}
|
||||||
|
|
||||||
return openDetails.channel, openDetails.blockHeight, openDetails.txIndex
|
openDetails := <-r.chanOpen
|
||||||
|
return &OpenChannelDetails{
|
||||||
|
Channel: openDetails.channel,
|
||||||
|
ConfirmationHeight: openDetails.blockHeight,
|
||||||
|
TransactionIndex: openDetails.txIndex,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FinalizeReservation completes the pending reservation, returning an active
|
// FinalizeReservation completes the pending reservation, returning an active
|
||||||
@ -463,5 +487,9 @@ func (r *ChannelReservation) FinalizeReservation() (*LightningChannel, error) {
|
|||||||
err: errChan,
|
err: errChan,
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<-r.chanOpen).channel, <-errChan
|
if err := <-errChan; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<-r.chanOpen).channel, nil
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package lnwallet
|
package lnwallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
@ -1229,13 +1230,18 @@ func (l *LightningWallet) handleChannelOpen(req *channelOpenMsg) {
|
|||||||
|
|
||||||
// Finally, create and officially open the payment channel!
|
// Finally, create and officially open the payment channel!
|
||||||
// TODO(roasbeef): CreationTime once tx is 'open'
|
// TODO(roasbeef): CreationTime once tx is 'open'
|
||||||
channel, _ := NewLightningChannel(l.Signer, l.ChainIO, l.chainNotifier,
|
channel, err := NewLightningChannel(l.Signer, l.ChainIO, l.chainNotifier,
|
||||||
res.partialState)
|
res.partialState)
|
||||||
|
if err != nil {
|
||||||
|
req.err <- err
|
||||||
|
res.chanOpen <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.err <- nil
|
||||||
res.chanOpen <- &openChanDetails{
|
res.chanOpen <- &openChanDetails{
|
||||||
channel: channel,
|
channel: channel,
|
||||||
}
|
}
|
||||||
req.err <- nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// openChannelAfterConfirmations creates, and opens a payment channel after
|
// openChannelAfterConfirmations creates, and opens a payment channel after
|
||||||
@ -1265,20 +1271,28 @@ out:
|
|||||||
// don't count this as the signal that the funding transaction has
|
// don't count this as the signal that the funding transaction has
|
||||||
// been confirmed.
|
// been confirmed.
|
||||||
if !ok {
|
if !ok {
|
||||||
|
res.chanOpenErr <- errors.New("wallet shutting down")
|
||||||
res.chanOpen <- nil
|
res.chanOpen <- nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
break out
|
break out
|
||||||
case <-l.quit:
|
case <-l.quit:
|
||||||
|
res.chanOpenErr <- errors.New("wallet shutting down")
|
||||||
res.chanOpen <- nil
|
res.chanOpen <- nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, create and officially open the payment channel!
|
// Finally, create and officially open the payment channel!
|
||||||
// TODO(roasbeef): CreationTime once tx is 'open'
|
// TODO(roasbeef): CreationTime once tx is 'open'
|
||||||
channel, _ := NewLightningChannel(l.Signer, l.ChainIO, l.chainNotifier,
|
channel, err := NewLightningChannel(l.Signer, l.ChainIO, l.chainNotifier,
|
||||||
res.partialState)
|
res.partialState)
|
||||||
|
if err != nil {
|
||||||
|
res.chanOpenErr <- err
|
||||||
|
res.chanOpen <- nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res.chanOpenErr <- nil
|
||||||
res.chanOpen <- &openChanDetails{
|
res.chanOpen <- &openChanDetails{
|
||||||
channel: channel,
|
channel: channel,
|
||||||
blockHeight: confDetails.BlockHeight,
|
blockHeight: confDetails.BlockHeight,
|
||||||
|
Loading…
Reference in New Issue
Block a user