diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index bc3642fd..3e8c5f22 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -372,10 +372,9 @@ func createAnnouncements(blockHeight uint32) (*annBatch, error) { } func createNodeAnnouncement(priv *btcec.PrivateKey, - timestamp uint32) (*lnwire.NodeAnnouncement, - error) { - var err error + timestamp uint32, extraBytes ...[]byte) (*lnwire.NodeAnnouncement, error) { + var err error k := hex.EncodeToString(priv.Serialize()) alias, err := lnwire.NewNodeAlias("kek" + k[:10]) if err != nil { @@ -389,6 +388,9 @@ func createNodeAnnouncement(priv *btcec.PrivateKey, Features: testFeatures, } copy(a.NodeID[:], priv.PubKey().SerializeCompressed()) + if len(extraBytes) == 1 { + a.ExtraOpaqueData = extraBytes[0] + } signer := mockSigner{priv} sig, err := SignAnnouncement(&signer, priv.PubKey(), a) @@ -405,8 +407,8 @@ func createNodeAnnouncement(priv *btcec.PrivateKey, } func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, - nodeKey *btcec.PrivateKey, timestamp uint32) (*lnwire.ChannelUpdate, - error) { + nodeKey *btcec.PrivateKey, timestamp uint32, + extraBytes ...[]byte) (*lnwire.ChannelUpdate, error) { var err error @@ -421,6 +423,9 @@ func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, FeeRate: uint32(prand.Int31()), BaseFee: uint32(prand.Int31()), } + if len(extraBytes) == 1 { + a.ExtraOpaqueData = extraBytes[0] + } pub := nodeKey.PubKey() signer := mockSigner{nodeKey} @@ -437,7 +442,8 @@ func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, return a, nil } -func createRemoteChannelAnnouncement(blockHeight uint32) (*lnwire.ChannelAnnouncement, error) { +func createRemoteChannelAnnouncement(blockHeight uint32, + extraBytes ...[]byte) (*lnwire.ChannelAnnouncement, error) { var err error a := &lnwire.ChannelAnnouncement{ @@ -452,6 +458,9 @@ func createRemoteChannelAnnouncement(blockHeight uint32) (*lnwire.ChannelAnnounc copy(a.NodeID2[:], nodeKeyPub2.SerializeCompressed()) copy(a.BitcoinKey1[:], bitcoinKeyPub1.SerializeCompressed()) copy(a.BitcoinKey2[:], bitcoinKeyPub2.SerializeCompressed()) + if len(extraBytes) == 1 { + a.ExtraOpaqueData = extraBytes[0] + } pub := nodeKeyPriv1.PubKey() signer := mockSigner{nodeKeyPriv1} @@ -1736,7 +1745,6 @@ func TestDeDuplicatedAnnouncements(t *testing.T) { // same channel ID. Adding this shouldn't cause an increase in the // number of items as they should be de-duplicated. ca2, err := createRemoteChannelAnnouncement(0) - if err != nil { t.Fatalf("can't create remote channel announcement: %v", err) } @@ -2146,6 +2154,143 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) { } } +// TestExtraDataChannelAnnouncementValidation tests that we're able to properly +// validate a ChannelAnnouncement that includes opaque bytes that we don't +// currently know of. +func TestExtraDataChannelAnnouncementValidation(t *testing.T) { + t.Parallel() + + ctx, cleanup, err := createTestCtx(0) + if err != nil { + t.Fatalf("can't create context: %v", err) + } + defer cleanup() + + remotePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil} + + // We'll now create an announcement that contains an extra set of bytes + // that we don't know of ourselves, but should still include in the + // final signature check. + extraBytes := []byte("gotta validate this stil!") + ca, err := createRemoteChannelAnnouncement(0, extraBytes) + if err != nil { + t.Fatalf("can't create channel announcement: %v", err) + } + + // We'll now send the announcement to the main gossiper. We should be + // able to validate this announcement to problem. + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, remotePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err != nil { + t.Fatalf("unable to process :%v", err) + } +} + +// TestExtraDataChannelUpdateValidation tests that we're able to properly +// validate a ChannelUpdate that includes opaque bytes that we don't currently +// know of. +func TestExtraDataChannelUpdateValidation(t *testing.T) { + t.Parallel() + + ctx, cleanup, err := createTestCtx(0) + if err != nil { + t.Fatalf("can't create context: %v", err) + } + defer cleanup() + + remotePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil} + timestamp := uint32(123456) + + // In this scenario, we'll create two announcements, one regular + // channel announcement, and another channel update announcement, that + // has additional data that we won't be interpreting. + chanAnn, err := createRemoteChannelAnnouncement(0) + if err != nil { + t.Fatalf("unable to create chan ann: %v", err) + } + chanUpdAnn1, err := createUpdateAnnouncement( + 0, 0, nodeKeyPriv1, timestamp, + []byte("must also validate"), + ) + if err != nil { + t.Fatalf("unable to create chan up: %v", err) + } + chanUpdAnn2, err := createUpdateAnnouncement( + 0, 1, nodeKeyPriv2, timestamp, + []byte("must also validate"), + ) + if err != nil { + t.Fatalf("unable to create chan up: %v", err) + } + + // We should be able to properly validate all three messages without + // any issue. + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanAnn, remotePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err != nil { + t.Fatalf("unable to process announcement: %v", err) + } + + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn1, remotePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err != nil { + t.Fatalf("unable to process announcement: %v", err) + } + + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn2, remotePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err != nil { + t.Fatalf("unable to process announcement: %v", err) + } +} + +// TestExtraDataNodeAnnouncementValidation tests that we're able to properly +// validate a NodeAnnouncement that includes opaque bytes that we don't +// currently know of. +func TestExtraDataNodeAnnouncementValidation(t *testing.T) { + t.Parallel() + + ctx, cleanup, err := createTestCtx(0) + if err != nil { + t.Fatalf("can't create context: %v", err) + } + defer cleanup() + + remotePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil} + timestamp := uint32(123456) + + // We'll create a node announcement that includes a set of opaque data + // which we don't know of, but will store anyway in order to ensure + // upgrades can flow smoothly in the future. + nodeAnn, err := createNodeAnnouncement( + nodeKeyPriv1, timestamp, []byte("gotta validate"), + ) + if err != nil { + t.Fatalf("can't create node announcement: %v", err) + } + + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(nodeAnn, remotePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err != nil { + t.Fatalf("unable to process announcement: %v", err) + } +} + // mockPeer implements the lnpeer.Peer interface and is used to test the // gossiper's interaction with peers. type mockPeer struct {