discovery: support optional message fields when processing announcements
In this commit, we extend the gossiper with support for external callers to provide optional fields that can serve as useful when processing a specific network announcement. This will serve useful for light clients, which are unable to obtain the channel point and capacity for a given channel, but can provide them manually for their own set of channels.
This commit is contained in:
parent
5d3621cc83
commit
aed0c2a90e
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
@ -34,12 +35,49 @@ var (
|
|||||||
ErrGossipSyncerNotFound = errors.New("gossip syncer not found")
|
ErrGossipSyncerNotFound = errors.New("gossip syncer not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// optionalMsgFields is a set of optional message fields that external callers
|
||||||
|
// can provide that serve useful when processing a specific network
|
||||||
|
// announcement.
|
||||||
|
type optionalMsgFields struct {
|
||||||
|
capacity *btcutil.Amount
|
||||||
|
channelPoint *wire.OutPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply applies the optional fields within the functional options.
|
||||||
|
func (f *optionalMsgFields) apply(optionalMsgFields ...OptionalMsgField) {
|
||||||
|
for _, optionalMsgField := range optionalMsgFields {
|
||||||
|
optionalMsgField(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionalMsgField is a functional option parameter that can be used to provide
|
||||||
|
// external information that is not included within a network message but serves
|
||||||
|
// useful when processing it.
|
||||||
|
type OptionalMsgField func(*optionalMsgFields)
|
||||||
|
|
||||||
|
// ChannelCapacity is an optional field that lets the gossiper know of the
|
||||||
|
// capacity of a channel.
|
||||||
|
func ChannelCapacity(capacity btcutil.Amount) OptionalMsgField {
|
||||||
|
return func(f *optionalMsgFields) {
|
||||||
|
f.capacity = &capacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelPoint is an optional field that lets the gossiper know of the outpoint
|
||||||
|
// of a channel.
|
||||||
|
func ChannelPoint(op wire.OutPoint) OptionalMsgField {
|
||||||
|
return func(f *optionalMsgFields) {
|
||||||
|
f.channelPoint = &op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// networkMsg couples a routing related wire message with the peer that
|
// networkMsg couples a routing related wire message with the peer that
|
||||||
// originally sent it.
|
// originally sent it.
|
||||||
type networkMsg struct {
|
type networkMsg struct {
|
||||||
peer lnpeer.Peer
|
peer lnpeer.Peer
|
||||||
source *btcec.PublicKey
|
source *btcec.PublicKey
|
||||||
msg lnwire.Message
|
msg lnwire.Message
|
||||||
|
optionalMsgFields *optionalMsgFields
|
||||||
|
|
||||||
isRemote bool
|
isRemote bool
|
||||||
|
|
||||||
@ -572,13 +610,17 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message,
|
|||||||
// entire channel announcement and update messages will be re-constructed and
|
// entire channel announcement and update messages will be re-constructed and
|
||||||
// broadcast to the rest of the network.
|
// broadcast to the rest of the network.
|
||||||
func (d *AuthenticatedGossiper) ProcessLocalAnnouncement(msg lnwire.Message,
|
func (d *AuthenticatedGossiper) ProcessLocalAnnouncement(msg lnwire.Message,
|
||||||
source *btcec.PublicKey) chan error {
|
source *btcec.PublicKey, optionalFields ...OptionalMsgField) chan error {
|
||||||
|
|
||||||
|
optionalMsgFields := &optionalMsgFields{}
|
||||||
|
optionalMsgFields.apply(optionalFields...)
|
||||||
|
|
||||||
nMsg := &networkMsg{
|
nMsg := &networkMsg{
|
||||||
msg: msg,
|
msg: msg,
|
||||||
isRemote: false,
|
optionalMsgFields: optionalMsgFields,
|
||||||
source: source,
|
isRemote: false,
|
||||||
err: make(chan error, 1),
|
source: source,
|
||||||
|
err: make(chan error, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@ -1605,6 +1647,17 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
|||||||
ExtraOpaqueData: msg.ExtraOpaqueData,
|
ExtraOpaqueData: msg.ExtraOpaqueData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there were any optional message fields provided, we'll
|
||||||
|
// include them in its serialized disk representation now.
|
||||||
|
if nMsg.optionalMsgFields != nil {
|
||||||
|
if nMsg.optionalMsgFields.capacity != nil {
|
||||||
|
edge.Capacity = *nMsg.optionalMsgFields.capacity
|
||||||
|
}
|
||||||
|
if nMsg.optionalMsgFields.channelPoint != nil {
|
||||||
|
edge.ChannelPoint = *nMsg.optionalMsgFields.channelPoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We will add the edge to the channel router. If the nodes
|
// We will add the edge to the channel router. If the nodes
|
||||||
// present in this channel are not present in the database, a
|
// present in this channel are not present in the database, a
|
||||||
// partial node will be added to represent each node while we
|
// partial node will be added to represent each node while we
|
||||||
|
@ -146,9 +146,6 @@ func (r *mockGraphSource) AddEdge(info *channeldb.ChannelEdgeInfo) error {
|
|||||||
return errors.New("info already exist")
|
return errors.New("info already exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usually, the capacity is fetched in the router from the funding txout.
|
|
||||||
// Since the mockGraphSource can't access the txout, assign a default value.
|
|
||||||
info.Capacity = maxBtcFundingAmount
|
|
||||||
r.infos[info.ChannelID] = *info
|
r.infos[info.ChannelID] = *info
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -3267,18 +3264,21 @@ func TestSendChannelUpdateReliably(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sendLocalMsg(t *testing.T, ctx *testCtx, msg lnwire.Message,
|
func sendLocalMsg(t *testing.T, ctx *testCtx, msg lnwire.Message,
|
||||||
localPub *btcec.PublicKey) {
|
localPub *btcec.PublicKey, optionalMsgFields ...OptionalMsgField) {
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
var err error
|
||||||
select {
|
select {
|
||||||
case err := <-ctx.gossiper.ProcessLocalAnnouncement(msg, localPub):
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
||||||
if err != nil {
|
msg, localPub, optionalMsgFields...,
|
||||||
t.Fatalf("unable to process channel msg: %v", err)
|
):
|
||||||
}
|
|
||||||
case <-time.After(2 * time.Second):
|
case <-time.After(2 * time.Second):
|
||||||
t.Fatal("did not process local announcement")
|
t.Fatal("did not process local announcement")
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to process channel msg: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendRemoteMsg(t *testing.T, ctx *testCtx, msg lnwire.Message,
|
func sendRemoteMsg(t *testing.T, ctx *testCtx, msg lnwire.Message,
|
||||||
@ -3482,6 +3482,61 @@ out:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestProcessChannelAnnouncementOptionalMsgFields ensures that the gossiper can
|
||||||
|
// properly handled optional message fields provided by the caller when
|
||||||
|
// processing a channel announcement.
|
||||||
|
func TestProcessChannelAnnouncementOptionalMsgFields(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// We'll start by creating our test context and a set of test channel
|
||||||
|
// announcements.
|
||||||
|
ctx, cleanup, err := createTestCtx(0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create test context: %v", err)
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
chanAnn1 := createAnnouncementWithoutProof(100)
|
||||||
|
chanAnn2 := createAnnouncementWithoutProof(101)
|
||||||
|
localKey := nodeKeyPriv1.PubKey()
|
||||||
|
|
||||||
|
// assertOptionalMsgFields is a helper closure that ensures the optional
|
||||||
|
// message fields were set as intended.
|
||||||
|
assertOptionalMsgFields := func(chanID lnwire.ShortChannelID,
|
||||||
|
capacity btcutil.Amount, channelPoint wire.OutPoint) {
|
||||||
|
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
edge, _, _, err := ctx.router.GetChannelByID(chanID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get channel by id: %v", err)
|
||||||
|
}
|
||||||
|
if edge.Capacity != capacity {
|
||||||
|
t.Fatalf("expected capacity %v, got %v", capacity,
|
||||||
|
edge.Capacity)
|
||||||
|
}
|
||||||
|
if edge.ChannelPoint != channelPoint {
|
||||||
|
t.Fatalf("expected channel point %v, got %v",
|
||||||
|
channelPoint, edge.ChannelPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll process the first announcement without any optional fields. We
|
||||||
|
// should see the channel's capacity and outpoint have a zero value.
|
||||||
|
sendLocalMsg(t, ctx, chanAnn1, localKey)
|
||||||
|
assertOptionalMsgFields(chanAnn1.ShortChannelID, 0, wire.OutPoint{})
|
||||||
|
|
||||||
|
// Providing the capacity and channel point as optional fields should
|
||||||
|
// propagate them all the way down to the router.
|
||||||
|
capacity := btcutil.Amount(1000)
|
||||||
|
channelPoint := wire.OutPoint{Index: 1}
|
||||||
|
sendLocalMsg(
|
||||||
|
t, ctx, chanAnn2, localKey, ChannelCapacity(capacity),
|
||||||
|
ChannelPoint(channelPoint),
|
||||||
|
)
|
||||||
|
assertOptionalMsgFields(chanAnn2.ShortChannelID, capacity, channelPoint)
|
||||||
|
}
|
||||||
|
|
||||||
func assertMessage(t *testing.T, expected, got lnwire.Message) {
|
func assertMessage(t *testing.T, expected, got lnwire.Message) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user