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"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
@ -336,7 +337,29 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
||||
Index: 0,
|
||||
},
|
||||
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,
|
||||
}
|
||||
fmsg.peer.queueMsg(errMsg, nil)
|
||||
@ -979,18 +1002,27 @@ func (f *fundingManager) handleErrorGenericMsg(fmsg *fundingErrorMsg) {
|
||||
e := fmsg.err
|
||||
|
||||
switch e.Code {
|
||||
case lnwire.ErrorMaxPendingChannels:
|
||||
case lnwire.ErrMaxPendingChannels:
|
||||
fallthrough
|
||||
case lnwire.ErrSynchronizingChain:
|
||||
peerID := fmsg.peer.id
|
||||
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)
|
||||
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:
|
||||
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) {
|
||||
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
|
||||
@ -1124,7 +1124,7 @@ func testMaxPendingChannels(net *networkHarness, t *harnessTest) {
|
||||
_, err = net.OpenChannel(ctx, net.Alice, carol, amount, 0, 1)
|
||||
if err == nil {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,14 @@ func (e ErrorCode) ToGrpcCode() codes.Code {
|
||||
}
|
||||
|
||||
const (
|
||||
// ErrorMaxPendingChannels is returned by remote peer when the number
|
||||
// of active pending channels exceeds their maximum policy limit.
|
||||
ErrorMaxPendingChannels ErrorCode = 1
|
||||
// ErrMaxPendingChannels is returned by remote peer when the number of
|
||||
// active pending channels exceeds their maximum policy limit.
|
||||
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
|
||||
|
12
rpcserver.go
12
rpcserver.go
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
@ -331,6 +332,17 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
|
||||
"allocation(us=%v, them=%v) numconfs=%v", in.TargetPeerId,
|
||||
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
|
||||
// key object. For all sync call, byte slices are expected to be
|
||||
// encoded as hex strings.
|
||||
|
Loading…
Reference in New Issue
Block a user