lnd.xprv/chan_series.go
Olaoluwa Osuntokun 09d4660524
chanseries: fix panic bug by ensuring we access a non-nil edge
In this commit, we fix an existing but that would cause the daemon to at
times crash. Before this commit, we access the wrong edge, which would
possibly actually be nil, leading to a panic. In this commit we fix this
by ensuring we access the proper edge which is known to be non-nil at
this point in the control flow.
2018-09-06 16:48:55 -07:00

316 lines
9.7 KiB
Go

package main
import (
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
)
// chanSeries is an implementation of the discovery.ChannelGraphTimeSeries
// interface backed by the channeldb ChannelGraph database. We'll provide this
// implementation to the AuthenticatedGossiper so it can properly use the
// in-protocol channel range queries to quickly and efficiently synchronize our
// channel state with all peers.
type chanSeries struct {
graph *channeldb.ChannelGraph
}
// HighestChanID should return is the channel ID of the channel we know of
// that's furthest in the target chain. This channel will have a block height
// that's close to the current tip of the main chain as we know it. We'll use
// this to start our QueryChannelRange dance with the remote node.
//
// NOTE: This is part of the discovery.ChannelGraphTimeSeries interface.
func (c *chanSeries) HighestChanID(chain chainhash.Hash) (*lnwire.ShortChannelID, error) {
chanID, err := c.graph.HighestChanID()
if err != nil {
return nil, err
}
shortChanID := lnwire.NewShortChanIDFromInt(chanID)
return &shortChanID, nil
}
// UpdatesInHorizon returns all known channel and node updates with an update
// timestamp between the start time and end time. We'll use this to catch up a
// remote node to the set of channel updates that they may have missed out on
// within the target chain.
//
// NOTE: This is part of the discovery.ChannelGraphTimeSeries interface.
func (c *chanSeries) UpdatesInHorizon(chain chainhash.Hash,
startTime time.Time, endTime time.Time) ([]lnwire.Message, error) {
var updates []lnwire.Message
// First, we'll query for all the set of channels that have an update
// that falls within the specified horizon.
chansInHorizon, err := c.graph.ChanUpdatesInHorizon(
startTime, endTime,
)
if err != nil {
return nil, err
}
for _, channel := range chansInHorizon {
// If the channel hasn't been fully advertised yet, or is a
// private channel, then we'll skip it as we can't construct a
// full authentication proof if one is requested.
if channel.Info.AuthProof == nil {
continue
}
chanAnn, edge1, edge2, err := discovery.CreateChanAnnouncement(
channel.Info.AuthProof, channel.Info, channel.Policy1,
channel.Policy2,
)
if err != nil {
return nil, err
}
updates = append(updates, chanAnn)
if edge1 != nil {
updates = append(updates, edge1)
}
if edge2 != nil {
updates = append(updates, edge2)
}
}
// Next, we'll send out all the node announcements that have an update
// within the horizon as well. We send these second to ensure that they
// follow any active channels they have.
nodeAnnsInHorizon, err := c.graph.NodeUpdatesInHorizon(
startTime, endTime,
)
if err != nil {
return nil, err
}
for _, nodeAnn := range nodeAnnsInHorizon {
nodeUpdate, err := makeNodeAnn(&nodeAnn)
if err != nil {
return nil, err
}
updates = append(updates, nodeUpdate)
}
return updates, nil
}
// FilterKnownChanIDs takes a target chain, and a set of channel ID's, and
// returns a filtered set of chan ID's. This filtered set of chan ID's
// represents the ID's that we don't know of which were in the passed superSet.
//
// NOTE: This is part of the discovery.ChannelGraphTimeSeries interface.
func (c *chanSeries) FilterKnownChanIDs(chain chainhash.Hash,
superSet []lnwire.ShortChannelID) ([]lnwire.ShortChannelID, error) {
chanIDs := make([]uint64, 0, len(superSet))
for _, chanID := range superSet {
chanIDs = append(chanIDs, chanID.ToUint64())
}
newChanIDs, err := c.graph.FilterKnownChanIDs(chanIDs)
if err != nil {
return nil, err
}
filteredIDs := make([]lnwire.ShortChannelID, 0, len(newChanIDs))
for _, chanID := range newChanIDs {
filteredIDs = append(
filteredIDs, lnwire.NewShortChanIDFromInt(chanID),
)
}
return filteredIDs, nil
}
// FilterChannelRange returns the set of channels that we created between the
// start height and the end height. We'll use this respond to a remote peer's
// QueryChannelRange message.
//
// NOTE: This is part of the discovery.ChannelGraphTimeSeries interface.
func (c *chanSeries) FilterChannelRange(chain chainhash.Hash,
startHeight, endHeight uint32) ([]lnwire.ShortChannelID, error) {
chansInRange, err := c.graph.FilterChannelRange(startHeight, endHeight)
if err != nil {
return nil, err
}
chanResp := make([]lnwire.ShortChannelID, 0, len(chansInRange))
for _, chanID := range chansInRange {
chanResp = append(
chanResp, lnwire.NewShortChanIDFromInt(chanID),
)
}
return chanResp, nil
}
func makeNodeAnn(n *channeldb.LightningNode) (*lnwire.NodeAnnouncement, error) {
alias, _ := lnwire.NewNodeAlias(n.Alias)
wireSig, err := lnwire.NewSigFromRawSignature(n.AuthSigBytes)
if err != nil {
return nil, err
}
return &lnwire.NodeAnnouncement{
Signature: wireSig,
Timestamp: uint32(n.LastUpdate.Unix()),
Addresses: n.Addresses,
NodeID: n.PubKeyBytes,
Features: n.Features.RawFeatureVector,
RGBColor: n.Color,
Alias: alias,
ExtraOpaqueData: n.ExtraOpaqueData,
}, nil
}
// FetchChanAnns returns a full set of channel announcements as well as their
// updates that match the set of specified short channel ID's. We'll use this
// to reply to a QueryShortChanIDs message sent by a remote peer. The response
// will contain a unique set of ChannelAnnouncements, the latest ChannelUpdate
// for each of the announcements, and a unique set of NodeAnnouncements.
//
// NOTE: This is part of the discovery.ChannelGraphTimeSeries interface.
func (c *chanSeries) FetchChanAnns(chain chainhash.Hash,
shortChanIDs []lnwire.ShortChannelID) ([]lnwire.Message, error) {
chanIDs := make([]uint64, 0, len(shortChanIDs))
for _, chanID := range shortChanIDs {
chanIDs = append(chanIDs, chanID.ToUint64())
}
channels, err := c.graph.FetchChanInfos(chanIDs)
if err != nil {
return nil, err
}
// We'll use this map to ensure we don't send the same node
// announcement more than one time as one node may have many channel
// anns we'll need to send.
nodePubsSent := make(map[routing.Vertex]struct{})
chanAnns := make([]lnwire.Message, 0, len(channels)*3)
for _, channel := range channels {
// If the channel doesn't have an authentication proof, then we
// won't send it over as it may not yet be finalized, or be a
// non-advertised channel.
if channel.Info.AuthProof == nil {
continue
}
chanAnn, edge1, edge2, err := discovery.CreateChanAnnouncement(
channel.Info.AuthProof, channel.Info, channel.Policy1,
channel.Policy2,
)
if err != nil {
return nil, err
}
chanAnns = append(chanAnns, chanAnn)
if edge1 != nil {
chanAnns = append(chanAnns, edge1)
// If this edge has a validated node announcement, that
// we haven't yet sent, then we'll send that as well.
nodePub := channel.Policy1.Node.PubKeyBytes
hasNodeAnn := channel.Policy1.Node.HaveNodeAnnouncement
if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
nodeAnn, err := makeNodeAnn(channel.Policy1.Node)
if err != nil {
return nil, err
}
chanAnns = append(chanAnns, nodeAnn)
nodePubsSent[nodePub] = struct{}{}
}
}
if edge2 != nil {
chanAnns = append(chanAnns, edge2)
// If this edge has a validated node announcement, that
// we haven't yet sent, then we'll send that as well.
nodePub := channel.Policy2.Node.PubKeyBytes
hasNodeAnn := channel.Policy2.Node.HaveNodeAnnouncement
if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
nodeAnn, err := makeNodeAnn(channel.Policy2.Node)
if err != nil {
return nil, err
}
chanAnns = append(chanAnns, nodeAnn)
nodePubsSent[nodePub] = struct{}{}
}
}
}
return chanAnns, nil
}
// FetchChanUpdates returns the latest channel update messages for the
// specified short channel ID. If no channel updates are known for the channel,
// then an empty slice will be returned.
//
// NOTE: This is part of the discovery.ChannelGraphTimeSeries interface.
func (c *chanSeries) FetchChanUpdates(chain chainhash.Hash,
shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate, error) {
chanInfo, e1, e2, err := c.graph.FetchChannelEdgesByID(
shortChanID.ToUint64(),
)
if err != nil {
return nil, err
}
chanUpdates := make([]*lnwire.ChannelUpdate, 0, 2)
if e1 != nil {
chanUpdate := &lnwire.ChannelUpdate{
ChainHash: chanInfo.ChainHash,
ShortChannelID: shortChanID,
Timestamp: uint32(e1.LastUpdate.Unix()),
Flags: e1.Flags,
TimeLockDelta: e1.TimeLockDelta,
HtlcMinimumMsat: e1.MinHTLC,
BaseFee: uint32(e1.FeeBaseMSat),
FeeRate: uint32(e1.FeeProportionalMillionths),
ExtraOpaqueData: e1.ExtraOpaqueData,
}
chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(e1.SigBytes)
if err != nil {
return nil, err
}
chanUpdates = append(chanUpdates, chanUpdate)
}
if e2 != nil {
chanUpdate := &lnwire.ChannelUpdate{
ChainHash: chanInfo.ChainHash,
ShortChannelID: shortChanID,
Timestamp: uint32(e2.LastUpdate.Unix()),
Flags: e2.Flags,
TimeLockDelta: e2.TimeLockDelta,
HtlcMinimumMsat: e2.MinHTLC,
BaseFee: uint32(e2.FeeBaseMSat),
FeeRate: uint32(e2.FeeProportionalMillionths),
ExtraOpaqueData: e2.ExtraOpaqueData,
}
chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(e2.SigBytes)
if err != nil {
return nil, err
}
chanUpdates = append(chanUpdates, chanUpdate)
}
return chanUpdates, nil
}
// A compile-time assertion to ensure that chanSeries meets the
// discovery.ChannelGraphTimeSeries interface.
var _ discovery.ChannelGraphTimeSeries = (*chanSeries)(nil)