mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-21 14:10:35 +02:00
Merge pull request #1825 from Roasbeef/extra-gossip-message-data
channeldb+discovery: ensure we store, validate and propagate announcements with opaque data
This commit is contained in:
@@ -280,13 +280,14 @@ func (d *AuthenticatedGossiper) SynchronizeNode(syncPeer lnpeer.Peer) error {
|
||||
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,
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1618,6 +1619,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
||||
AuthSigBytes: msg.Signature.ToSignatureBytes(),
|
||||
Features: features,
|
||||
Color: msg.RGBColor,
|
||||
ExtraOpaqueData: msg.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
if err := d.cfg.Router.AddNode(node); err != nil {
|
||||
@@ -1740,6 +1742,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
||||
BitcoinKey2Bytes: msg.BitcoinKey2,
|
||||
AuthProof: proof,
|
||||
Features: featureBuf.Bytes(),
|
||||
ExtraOpaqueData: msg.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
// We will add the edge to the channel router. If the nodes
|
||||
@@ -2009,6 +2012,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
||||
MinHTLC: msg.HtlcMinimumMsat,
|
||||
FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee),
|
||||
FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate),
|
||||
ExtraOpaqueData: msg.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
if err := d.cfg.Router.UpdateEdge(update); err != nil {
|
||||
@@ -2477,6 +2481,7 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo,
|
||||
HtlcMinimumMsat: edge.MinHTLC,
|
||||
BaseFee: uint32(edge.FeeBaseMSat),
|
||||
FeeRate: uint32(edge.FeeProportionalMillionths),
|
||||
ExtraOpaqueData: edge.ExtraOpaqueData,
|
||||
}
|
||||
chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(edge.SigBytes)
|
||||
if err != nil {
|
||||
@@ -2518,13 +2523,14 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo,
|
||||
if info.AuthProof != nil {
|
||||
chanID := lnwire.NewShortChanIDFromInt(info.ChannelID)
|
||||
chanAnn = &lnwire.ChannelAnnouncement{
|
||||
ShortChannelID: chanID,
|
||||
NodeID1: info.NodeKey1Bytes,
|
||||
NodeID2: info.NodeKey2Bytes,
|
||||
ChainHash: info.ChainHash,
|
||||
BitcoinKey1: info.BitcoinKey1Bytes,
|
||||
Features: lnwire.NewRawFeatureVector(),
|
||||
BitcoinKey2: info.BitcoinKey2Bytes,
|
||||
ShortChannelID: chanID,
|
||||
NodeID1: info.NodeKey1Bytes,
|
||||
NodeID2: info.NodeKey2Bytes,
|
||||
ChainHash: info.ChainHash,
|
||||
BitcoinKey1: info.BitcoinKey1Bytes,
|
||||
Features: lnwire.NewRawFeatureVector(),
|
||||
BitcoinKey2: info.BitcoinKey2Bytes,
|
||||
ExtraOpaqueData: edge.ExtraOpaqueData,
|
||||
}
|
||||
chanAnn.NodeSig1, err = lnwire.NewSigFromRawSignature(
|
||||
info.AuthProof.NodeSig1Bytes,
|
||||
|
@@ -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 {
|
||||
|
@@ -23,13 +23,14 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof,
|
||||
// authenticated channel announcement.
|
||||
chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID)
|
||||
chanAnn := &lnwire.ChannelAnnouncement{
|
||||
ShortChannelID: chanID,
|
||||
NodeID1: chanInfo.NodeKey1Bytes,
|
||||
NodeID2: chanInfo.NodeKey2Bytes,
|
||||
ChainHash: chanInfo.ChainHash,
|
||||
BitcoinKey1: chanInfo.BitcoinKey1Bytes,
|
||||
BitcoinKey2: chanInfo.BitcoinKey2Bytes,
|
||||
Features: lnwire.NewRawFeatureVector(),
|
||||
ShortChannelID: chanID,
|
||||
NodeID1: chanInfo.NodeKey1Bytes,
|
||||
NodeID2: chanInfo.NodeKey2Bytes,
|
||||
ChainHash: chanInfo.ChainHash,
|
||||
BitcoinKey1: chanInfo.BitcoinKey1Bytes,
|
||||
BitcoinKey2: chanInfo.BitcoinKey2Bytes,
|
||||
Features: lnwire.NewRawFeatureVector(),
|
||||
ExtraOpaqueData: chanInfo.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -76,6 +77,7 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof,
|
||||
HtlcMinimumMsat: e1.MinHTLC,
|
||||
BaseFee: uint32(e1.FeeBaseMSat),
|
||||
FeeRate: uint32(e1.FeeProportionalMillionths),
|
||||
ExtraOpaqueData: e1.ExtraOpaqueData,
|
||||
}
|
||||
edge1Ann.Signature, err = lnwire.NewSigFromRawSignature(e1.SigBytes)
|
||||
if err != nil {
|
||||
@@ -92,6 +94,7 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof,
|
||||
HtlcMinimumMsat: e2.MinHTLC,
|
||||
BaseFee: uint32(e2.FeeBaseMSat),
|
||||
FeeRate: uint32(e2.FeeProportionalMillionths),
|
||||
ExtraOpaqueData: e2.ExtraOpaqueData,
|
||||
}
|
||||
edge2Ann.Signature, err = lnwire.NewSigFromRawSignature(e2.SigBytes)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user