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:
Olaoluwa Osuntokun 2017-01-24 17:12:51 -08:00
parent 73d5daa2c3
commit a658fabf48
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
4 changed files with 60 additions and 11 deletions

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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.