funding: disallow channel creation before lnd is synced to the chain
This commit adds a new restriction around funding channels at the daemon level: lnd nodes will not allow either the initiation or the acceptance of a channel before the node is fully synced to the best known chain. This fixes a class of bug that arises when a new node joins the network and either attempts to open a channel or has a channel extended to them before the node is fully synced to the network.
This commit is contained in:
parent
73d5daa2c3
commit
a658fabf48
@ -7,6 +7,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@ -336,7 +337,29 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
|||||||
Index: 0,
|
Index: 0,
|
||||||
},
|
},
|
||||||
Problem: "Number of pending channels exceed maximum",
|
Problem: "Number of pending channels exceed maximum",
|
||||||
Code: lnwire.ErrorMaxPendingChannels,
|
Code: lnwire.ErrMaxPendingChannels,
|
||||||
|
PendingChannelID: fmsg.msg.ChannelID,
|
||||||
|
}
|
||||||
|
fmsg.peer.queueMsg(errMsg, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll also reject any requests to create channels until we're fully
|
||||||
|
// synced to the network as we won't be able to properly validate the
|
||||||
|
// confirmation of the funding transaction.
|
||||||
|
isSynced, err := f.wallet.IsSynced()
|
||||||
|
if err != nil {
|
||||||
|
fndgLog.Errorf("unable to query wallet: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !isSynced {
|
||||||
|
errMsg := &lnwire.ErrorGeneric{
|
||||||
|
ChannelPoint: &wire.OutPoint{
|
||||||
|
Hash: chainhash.Hash{},
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
|
Problem: "Synchronizing blockchain",
|
||||||
|
Code: lnwire.ErrSynchronizingChain,
|
||||||
PendingChannelID: fmsg.msg.ChannelID,
|
PendingChannelID: fmsg.msg.ChannelID,
|
||||||
}
|
}
|
||||||
fmsg.peer.queueMsg(errMsg, nil)
|
fmsg.peer.queueMsg(errMsg, nil)
|
||||||
@ -979,18 +1002,27 @@ func (f *fundingManager) handleErrorGenericMsg(fmsg *fundingErrorMsg) {
|
|||||||
e := fmsg.err
|
e := fmsg.err
|
||||||
|
|
||||||
switch e.Code {
|
switch e.Code {
|
||||||
case lnwire.ErrorMaxPendingChannels:
|
case lnwire.ErrMaxPendingChannels:
|
||||||
|
fallthrough
|
||||||
|
case lnwire.ErrSynchronizingChain:
|
||||||
peerID := fmsg.peer.id
|
peerID := fmsg.peer.id
|
||||||
chanID := fmsg.err.PendingChannelID
|
chanID := fmsg.err.PendingChannelID
|
||||||
|
|
||||||
if ctx, err := f.cancelReservationCtx(peerID, chanID); err != nil {
|
resCtx, err := f.cancelReservationCtx(peerID, chanID)
|
||||||
|
if err != nil {
|
||||||
fndgLog.Warnf("unable to delete reservation: %v", err)
|
fndgLog.Warnf("unable to delete reservation: %v", err)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
ctx.err <- grpc.Errorf(e.Code.ToGrpcCode(), e.Problem)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fndgLog.Errorf("Received funding error from %v: %v", fmsg.peer,
|
||||||
|
newLogClosure(func() string {
|
||||||
|
return spew.Sdump(e)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
resCtx.err <- grpc.Errorf(e.Code.ToGrpcCode(), e.Problem)
|
||||||
|
return
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fndgLog.Warnf("unknown funding error (%v:%v)", e.Code, e.Problem)
|
fndgLog.Warnf("unknown funding error (%v:%v)", e.Code, e.Problem)
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ func testBasicChannelFunding(net *networkHarness, t *harnessTest) {
|
|||||||
}
|
}
|
||||||
if bobBal.Balance != int64(pushAmt) {
|
if bobBal.Balance != int64(pushAmt) {
|
||||||
t.Fatalf("bob's balance is incorrect: expected %v got %v",
|
t.Fatalf("bob's balance is incorrect: expected %v got %v",
|
||||||
pushAmt, bobBal)
|
pushAmt, bobBal.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, immediately close the channel. This function will also
|
// Finally, immediately close the channel. This function will also
|
||||||
@ -1124,7 +1124,7 @@ func testMaxPendingChannels(net *networkHarness, t *harnessTest) {
|
|||||||
_, err = net.OpenChannel(ctx, net.Alice, carol, amount, 0, 1)
|
_, err = net.OpenChannel(ctx, net.Alice, carol, amount, 0, 1)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("error wasn't received")
|
t.Fatalf("error wasn't received")
|
||||||
} else if grpc.Code(err) != lnwire.ErrorMaxPendingChannels.ToGrpcCode() {
|
} else if grpc.Code(err) != lnwire.ErrMaxPendingChannels.ToGrpcCode() {
|
||||||
t.Fatalf("not expected error was received: %v", err)
|
t.Fatalf("not expected error was received: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,9 +21,14 @@ func (e ErrorCode) ToGrpcCode() codes.Code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ErrorMaxPendingChannels is returned by remote peer when the number
|
// ErrMaxPendingChannels is returned by remote peer when the number of
|
||||||
// of active pending channels exceeds their maximum policy limit.
|
// active pending channels exceeds their maximum policy limit.
|
||||||
ErrorMaxPendingChannels ErrorCode = 1
|
ErrMaxPendingChannels ErrorCode = 1
|
||||||
|
|
||||||
|
// ErrSynchronizingChain is returned by a remote peer that receives a
|
||||||
|
// channel update or a funding request while their still syncing to the
|
||||||
|
// latest state of the blockchain.
|
||||||
|
ErrSynchronizingChain ErrorCode = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorGeneric represents a generic error bound to an exact channel. The
|
// ErrorGeneric represents a generic error bound to an exact channel. The
|
||||||
|
12
rpcserver.go
12
rpcserver.go
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
@ -331,6 +332,17 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
|
|||||||
"allocation(us=%v, them=%v) numconfs=%v", in.TargetPeerId,
|
"allocation(us=%v, them=%v) numconfs=%v", in.TargetPeerId,
|
||||||
in.LocalFundingAmount, in.PushSat, in.NumConfs)
|
in.LocalFundingAmount, in.PushSat, in.NumConfs)
|
||||||
|
|
||||||
|
// Creation of channels before the wallet syncs up is currently
|
||||||
|
// disallowed.
|
||||||
|
isSynced, err := r.server.lnwallet.IsSynced()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !isSynced {
|
||||||
|
return nil, errors.New("channels cannot be created before the " +
|
||||||
|
"wallet is fully synced")
|
||||||
|
}
|
||||||
|
|
||||||
// Decode the provided target node's public key, parsing it into a pub
|
// Decode the provided target node's public key, parsing it into a pub
|
||||||
// key object. For all sync call, byte slices are expected to be
|
// key object. For all sync call, byte slices are expected to be
|
||||||
// encoded as hex strings.
|
// encoded as hex strings.
|
||||||
|
Loading…
Reference in New Issue
Block a user