From 47e700ba9ec0aa68999317cb5143d2c30426200e Mon Sep 17 00:00:00 2001 From: carla Date: Tue, 17 Dec 2019 17:36:27 +0200 Subject: [PATCH] rpcserver: return uptime errors for known channels Upgrade logging of channel uptime/lifespan query errors to returning the error, with the exception of the case where the channel is known to the store. This error is logged, due to the potential for race conditions between the channel event store being notified of a new open channel and the rpc channel subscription creating a rpc channel struct (which queries for uptime). --- rpcserver.go | 86 +++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index 97367db3..120418c7 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -32,6 +32,7 @@ import ( "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/chanacceptor" "github.com/lightningnetwork/lnd/chanbackup" + "github.com/lightningnetwork/lnd/chanfitness" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" "github.com/lightningnetwork/lnd/contractcourt" @@ -2752,7 +2753,10 @@ func (r *rpcServer) ListChannels(ctx context.Context, // Next, we'll determine whether we should add this channel to // our list depending on the type of channels requested to us. isActive := peerOnline && linkActive - channel := createRPCOpenChannel(r, graph, dbChannel, isActive) + channel, err := createRPCOpenChannel(r, graph, dbChannel, isActive) + if err != nil { + return nil, err + } // We'll only skip returning this channel if we were requested // for a specific kind and this channel doesn't satisfy it. @@ -2775,7 +2779,7 @@ func (r *rpcServer) ListChannels(ctx context.Context, // createRPCOpenChannel creates an *lnrpc.Channel from the *channeldb.Channel. func createRPCOpenChannel(r *rpcServer, graph *channeldb.ChannelGraph, - dbChannel *channeldb.OpenChannel, isActive bool) *lnrpc.Channel { + dbChannel *channeldb.OpenChannel, isActive bool) (*lnrpc.Channel, error) { nodePub := dbChannel.IdentityPub nodeID := hex.EncodeToString(nodePub.SerializeCompressed()) @@ -2810,43 +2814,12 @@ func createRPCOpenChannel(r *rpcServer, graph *channeldb.ChannelGraph, } externalCommitFee := dbChannel.Capacity - sumOutputs - chanID := dbChannel.ShortChannelID.ToUint64() - - var ( - uptime time.Duration - lifespan time.Duration - ) - - // Get the lifespan observed by the channel event store. - startTime, endTime, err := r.server.chanEventStore.GetLifespan(chanID) - if err != nil { - // If the channel cannot be found, log an error and do not perform - // further calculations for uptime and lifespan. - rpcsLog.Warnf("GetLifespan %v error: %v", chanID, err) - } else { - // If endTime is zero, the channel is still open, progress endTime to - // the present so we can calculate lifespan. - if endTime.IsZero() { - endTime = time.Now() - } - lifespan = endTime.Sub(startTime) - - uptime, err = r.server.chanEventStore.GetUptime( - chanID, - startTime, - endTime, - ) - if err != nil { - rpcsLog.Warnf("GetUptime %v error: %v", chanID, err) - } - } - channel := &lnrpc.Channel{ Active: isActive, Private: !isPublic, RemotePubkey: nodeID, ChannelPoint: chanPoint.String(), - ChanId: chanID, + ChanId: dbChannel.ShortChannelID.ToUint64(), Capacity: int64(dbChannel.Capacity), LocalBalance: int64(localBalance.ToSatoshis()), RemoteBalance: int64(remoteBalance.ToSatoshis()), @@ -2863,8 +2836,6 @@ func createRPCOpenChannel(r *rpcServer, graph *channeldb.ChannelGraph, LocalChanReserveSat: int64(dbChannel.LocalChanCfg.ChanReserve), RemoteChanReserveSat: int64(dbChannel.RemoteChanCfg.ChanReserve), StaticRemoteKey: dbChannel.ChanType.IsTweakless(), - Lifetime: int64(lifespan.Seconds()), - Uptime: int64(uptime.Seconds()), } for i, htlc := range localCommit.Htlcs { @@ -2881,7 +2852,42 @@ func createRPCOpenChannel(r *rpcServer, graph *channeldb.ChannelGraph, channel.UnsettledBalance += channel.PendingHtlcs[i].Amount } - return channel + // Get the lifespan observed by the channel event store. If the channel is + // not known to the channel event store, return early because we cannot + // calculate any further uptime information. + startTime, endTime, err := r.server.chanEventStore.GetLifespan(channel.ChanId) + switch err { + case chanfitness.ErrChannelNotFound: + rpcsLog.Infof("channel: %v not found by channel event store", + channel.ChanId) + + return channel, nil + case nil: + // If there is no error getting lifespan, continue to uptime + // calculation. + default: + return nil, err + } + + // If endTime is zero, the channel is still open, progress endTime to + // the present so we can calculate lifetime. + if endTime.IsZero() { + endTime = time.Now() + } + channel.Lifetime = int64(endTime.Sub(startTime).Seconds()) + + // Once we have successfully obtained channel lifespan, we know that the + // channel is known to the event store, so we can return any non-nil error + // that occurs. + uptime, err := r.server.chanEventStore.GetUptime( + channel.ChanId, startTime, endTime, + ) + if err != nil { + return nil, err + } + channel.Uptime = int64(uptime.Seconds()) + + return channel, nil } // createRPCClosedChannel creates an *lnrpc.ClosedChannelSummary from a @@ -2947,8 +2953,12 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription, var update *lnrpc.ChannelEventUpdate switch event := e.(type) { case channelnotifier.OpenChannelEvent: - channel := createRPCOpenChannel(r, graph, + channel, err := createRPCOpenChannel(r, graph, event.Channel, true) + if err != nil { + return err + } + update = &lnrpc.ChannelEventUpdate{ Type: lnrpc.ChannelEventUpdate_OPEN_CHANNEL, Channel: &lnrpc.ChannelEventUpdate_OpenChannel{