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/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
@ -34,12 +35,49 @@ var (
|
||||
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
|
||||
// originally sent it.
|
||||
type networkMsg struct {
|
||||
peer lnpeer.Peer
|
||||
source *btcec.PublicKey
|
||||
msg lnwire.Message
|
||||
peer lnpeer.Peer
|
||||
source *btcec.PublicKey
|
||||
msg lnwire.Message
|
||||
optionalMsgFields *optionalMsgFields
|
||||
|
||||
isRemote bool
|
||||
|
||||
@ -572,13 +610,17 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message,
|
||||
// entire channel announcement and update messages will be re-constructed and
|
||||
// broadcast to the rest of the network.
|
||||
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{
|
||||
msg: msg,
|
||||
isRemote: false,
|
||||
source: source,
|
||||
err: make(chan error, 1),
|
||||
msg: msg,
|
||||
optionalMsgFields: optionalMsgFields,
|
||||
isRemote: false,
|
||||
source: source,
|
||||
err: make(chan error, 1),
|
||||
}
|
||||
|
||||
select {
|
||||
@ -1605,6 +1647,17 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
||||
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
|
||||
// present in this channel are not present in the database, a
|
||||
// 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")
|
||||
}
|
||||
|
||||
// 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
|
||||
return nil
|
||||
}
|
||||
@ -3267,18 +3264,21 @@ func TestSendChannelUpdateReliably(t *testing.T) {
|
||||
}
|
||||
|
||||
func sendLocalMsg(t *testing.T, ctx *testCtx, msg lnwire.Message,
|
||||
localPub *btcec.PublicKey) {
|
||||
localPub *btcec.PublicKey, optionalMsgFields ...OptionalMsgField) {
|
||||
|
||||
t.Helper()
|
||||
|
||||
var err error
|
||||
select {
|
||||
case err := <-ctx.gossiper.ProcessLocalAnnouncement(msg, localPub):
|
||||
if err != nil {
|
||||
t.Fatalf("unable to process channel msg: %v", err)
|
||||
}
|
||||
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
||||
msg, localPub, optionalMsgFields...,
|
||||
):
|
||||
case <-time.After(2 * time.Second):
|
||||
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,
|
||||
@ -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) {
|
||||
t.Helper()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user