diff --git a/autopilot/prefattach_test.go b/autopilot/prefattach_test.go index 4e1bbf7b1..ba79d9682 100644 --- a/autopilot/prefattach_test.go +++ b/autopilot/prefattach_test.go @@ -486,6 +486,7 @@ func (d *testDBGraph) addRandChannel(node1, node2 *btcec.PublicKey, edge := &models.ChannelEdgeInfo{ ChannelID: chanID.ToUint64(), Capacity: capacity, + Features: lnwire.EmptyFeatureVector(), } edge.AddNodeKeys(lnNode1, lnNode2, lnNode1, lnNode2) if err := d.db.AddChannelEdge(ctx, edge); err != nil { diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 3b98b4caa..2722e5ca2 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -2648,13 +2648,6 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context, // With the proof validated (if necessary), we can now store it within // the database for our path finding and syncing needs. - var featureBuf bytes.Buffer - if err := ann.Features.Encode(&featureBuf); err != nil { - log.Errorf("unable to encode features: %v", err) - nMsg.err <- err - return nil, false - } - edge := &models.ChannelEdgeInfo{ ChannelID: scid.ToUint64(), ChainHash: ann.ChainHash, @@ -2663,8 +2656,10 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context, BitcoinKey1Bytes: ann.BitcoinKey1, BitcoinKey2Bytes: ann.BitcoinKey2, AuthProof: proof, - Features: featureBuf.Bytes(), - ExtraOpaqueData: ann.ExtraOpaqueData, + Features: lnwire.NewFeatureVector( + ann.Features, lnwire.Features, + ), + ExtraOpaqueData: ann.ExtraOpaqueData, } // If there were any optional message fields provided, we'll include diff --git a/graph/builder_test.go b/graph/builder_test.go index a7bd5ed91..4d1b4ef39 100644 --- a/graph/builder_test.go +++ b/graph/builder_test.go @@ -71,6 +71,7 @@ func TestAddProof(t *testing.T) { NodeKey1Bytes: node1.PubKeyBytes, NodeKey2Bytes: node2.PubKeyBytes, AuthProof: nil, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some(script), } copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) @@ -158,6 +159,7 @@ func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) { BitcoinKey1Bytes: pub1, BitcoinKey2Bytes: pub2, AuthProof: nil, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some(script), } edgePolicy := &models.ChannelEdgePolicy{ @@ -281,6 +283,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some(fundingScript1), } copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) @@ -300,6 +303,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some(fundingScript2), } copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) @@ -491,6 +495,7 @@ func TestDisconnectedBlocks(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some([]byte{}), } copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) @@ -512,6 +517,7 @@ func TestDisconnectedBlocks(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some([]byte{}), } copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) @@ -645,6 +651,7 @@ func TestChansClosedOfflinePruneGraph(t *testing.T) { }, ChannelPoint: *chanUTXO, Capacity: chanValue, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some(script), } copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) @@ -1062,6 +1069,7 @@ func TestIsStaleNode(t *testing.T) { BitcoinKey1Bytes: pub1, BitcoinKey2Bytes: pub2, AuthProof: nil, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some(script), } if err := ctx.builder.AddEdge(ctxb, edge); err != nil { @@ -1142,6 +1150,7 @@ func TestIsKnownEdge(t *testing.T) { BitcoinKey2Bytes: pub2, AuthProof: nil, FundingScript: fn.Some(script), + Features: lnwire.EmptyFeatureVector(), } if err := ctx.builder.AddEdge(ctxb, edge); err != nil { t.Fatalf("unable to add edge: %v", err) @@ -1200,6 +1209,7 @@ func TestIsStaleEdgePolicy(t *testing.T) { BitcoinKey1Bytes: pub1, BitcoinKey2Bytes: pub2, AuthProof: nil, + Features: lnwire.EmptyFeatureVector(), FundingScript: fn.Some(script), } if err := ctx.builder.AddEdge(ctxb, edge); err != nil { @@ -1508,6 +1518,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) ( AuthProof: &testAuthProof, ChannelPoint: fundingPoint, Capacity: btcutil.Amount(edge.Capacity), + Features: lnwire.EmptyFeatureVector(), } copy(edgeInfo.NodeKey1Bytes[:], node1Bytes) @@ -1882,6 +1893,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool, BitcoinKey1Bytes: node1Vertex, NodeKey2Bytes: node2Vertex, BitcoinKey2Bytes: node2Vertex, + Features: lnwire.EmptyFeatureVector(), } err = graph.AddChannelEdge(ctx, &edgeInfo) diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go index 67e3c32dc..fefc125c4 100644 --- a/graph/db/graph_test.go +++ b/graph/db/graph_test.go @@ -416,6 +416,7 @@ func TestEdgeInsertionDeletion(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), ChannelPoint: outpoint, Capacity: 9000, } @@ -479,12 +480,6 @@ func createEdge(height, txIndex uint32, txPosition uint16, outPointIndex uint32, Index: outPointIndex, } - var ( - features = lnwire.NewRawFeatureVector() - featureBuf bytes.Buffer - ) - _ = features.Encode(&featureBuf) - node1Pub, _ := node1.PubKey() node2Pub, _ := node2.PubKey() edgeInfo := models.ChannelEdgeInfo{ @@ -499,7 +494,7 @@ func createEdge(height, txIndex uint32, txPosition uint16, outPointIndex uint32, ChannelPoint: outpoint, Capacity: 9000, ExtraOpaqueData: make([]byte, 0), - Features: featureBuf.Bytes(), + Features: lnwire.EmptyFeatureVector(), } copy(edgeInfo.NodeKey1Bytes[:], node1Pub.SerializeCompressed()) @@ -663,8 +658,8 @@ func assertEdgeInfoEqual(t *testing.T, e1 *models.ChannelEdgeInfo, t.Fatalf("bitcoinkey2 doesn't match") } - if !bytes.Equal(e1.Features, e2.Features) { - t.Fatalf("features doesn't match: %x vs %x", e1.Features, + if !e1.Features.Equals(e2.Features.RawFeatureVector) { + t.Fatalf("features don't match: %v vs %v", e1.Features, e2.Features) } @@ -741,12 +736,6 @@ func createChannelEdge(node1, node2 *models.LightningNode, Index: prand.Uint32(), } - var ( - features = lnwire.NewRawFeatureVector() - featureBuf bytes.Buffer - ) - _ = features.Encode(&featureBuf) - // Add the new edge to the database, this should proceed without any // errors. edgeInfo := &models.ChannelEdgeInfo{ @@ -759,7 +748,7 @@ func createChannelEdge(node1, node2 *models.LightningNode, 2, 2, 2, 2, 3, 3, 3, 3, 3, }, - Features: featureBuf.Bytes(), + Features: lnwire.EmptyFeatureVector(), } copy(edgeInfo.NodeKey1Bytes[:], firstNode[:]) copy(edgeInfo.NodeKey2Bytes[:], secondNode[:]) @@ -1617,6 +1606,7 @@ func fillTestGraph(t testing.TB, graph *ChannelGraph, numNodes, BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), ChannelPoint: op, Capacity: 1000, } @@ -1799,6 +1789,7 @@ func TestGraphPruning(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), ChannelPoint: op, Capacity: 1000, } diff --git a/graph/db/kv_store.go b/graph/db/kv_store.go index eef4f7ec5..42b9f9245 100644 --- a/graph/db/kv_store.go +++ b/graph/db/kv_store.go @@ -4291,7 +4291,12 @@ func putChanEdgeInfo(edgeIndex kvdb.RwBucket, return err } - if err := wire.WriteVarBytes(&b, 0, edgeInfo.Features); err != nil { + var featureBuf bytes.Buffer + if err := edgeInfo.Features.Encode(&featureBuf); err != nil { + return fmt.Errorf("unable to encode features: %w", err) + } + + if err := wire.WriteVarBytes(&b, 0, featureBuf.Bytes()); err != nil { return err } @@ -4374,11 +4379,19 @@ func deserializeChanEdgeInfo(r io.Reader) (models.ChannelEdgeInfo, error) { return models.ChannelEdgeInfo{}, err } - edgeInfo.Features, err = wire.ReadVarBytes(r, 0, 900, "features") + featureBytes, err := wire.ReadVarBytes(r, 0, 900, "features") if err != nil { return models.ChannelEdgeInfo{}, err } + features := lnwire.NewRawFeatureVector() + err = features.Decode(bytes.NewReader(featureBytes)) + if err != nil { + return models.ChannelEdgeInfo{}, fmt.Errorf("unable to decode "+ + "features: %w", err) + } + edgeInfo.Features = lnwire.NewFeatureVector(features, lnwire.Features) + proof := &models.ChannelAuthProof{} proof.NodeSig1Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs") diff --git a/graph/db/models/channel_edge_info.go b/graph/db/models/channel_edge_info.go index 39e3b196f..d19287571 100644 --- a/graph/db/models/channel_edge_info.go +++ b/graph/db/models/channel_edge_info.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/fn/v2" + "github.com/lightningnetwork/lnd/lnwire" ) // ChannelEdgeInfo represents a fully authenticated channel along with all its @@ -46,9 +47,9 @@ type ChannelEdgeInfo struct { BitcoinKey2Bytes [33]byte bitcoinKey2 *btcec.PublicKey - // Features is an opaque byte slice that encodes the set of channel - // specific features that this channel edge supports. - Features []byte + // Features is the list of protocol features supported by this channel + // edge. + Features *lnwire.FeatureVector // AuthProof is the authentication proof for this channel. This proof // contains a set of signatures binding four identities, which attests diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go index a9de05ae1..c39bc6bbd 100644 --- a/graph/db/sql_store.go +++ b/graph/db/sql_store.go @@ -3833,26 +3833,16 @@ func insertChannel(ctx context.Context, db SQLQueries, } // Insert any channel features. - if len(edge.Features) != 0 { - chanFeatures := lnwire.NewRawFeatureVector() - err := chanFeatures.Decode(bytes.NewReader(edge.Features)) + for feature := range edge.Features.Features() { + err = db.InsertChannelFeature( + ctx, sqlc.InsertChannelFeatureParams{ + ChannelID: dbChanID, + FeatureBit: int32(feature), + }, + ) if err != nil { - return nil, err - } - - fv := lnwire.NewFeatureVector(chanFeatures, lnwire.Features) - for feature := range fv.Features() { - err = db.InsertChannelFeature( - ctx, sqlc.InsertChannelFeatureParams{ - ChannelID: dbChanID, - FeatureBit: int32(feature), - }, - ) - if err != nil { - return nil, fmt.Errorf("unable to insert "+ - "channel(%d) feature(%v): %w", dbChanID, - feature, err) - } + return nil, fmt.Errorf("unable to insert channel(%d) "+ + "feature(%v): %w", dbChanID, feature, err) } } @@ -3975,11 +3965,6 @@ func getAndBuildEdgeInfo(ctx context.Context, db SQLQueries, return nil, err } - var featureBuf bytes.Buffer - if err := fv.Encode(&featureBuf); err != nil { - return nil, fmt.Errorf("unable to encode features: %w", err) - } - recs, err := lnwire.CustomRecords(extras).Serialize() if err != nil { return nil, fmt.Errorf("unable to serialize extra signed "+ @@ -4002,7 +3987,7 @@ func getAndBuildEdgeInfo(ctx context.Context, db SQLQueries, BitcoinKey2Bytes: btcKey2, ChannelPoint: *op, Capacity: btcutil.Amount(dbChan.Capacity.Int64), - Features: featureBuf.Bytes(), + Features: fv, ExtraOpaqueData: recs, } diff --git a/graph/notifications_test.go b/graph/notifications_test.go index e7d081c63..4d847bdd2 100644 --- a/graph/notifications_test.go +++ b/graph/notifications_test.go @@ -458,6 +458,7 @@ func TestEdgeUpdateNotification(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), ChannelPoint: *chanPoint, Capacity: chanValue, FundingScript: fn.Some(script), @@ -645,6 +646,7 @@ func TestNodeUpdateNotification(t *testing.T) { ChannelID: chanID.ToUint64(), NodeKey1Bytes: node1.PubKeyBytes, NodeKey2Bytes: node2.PubKeyBytes, + Features: lnwire.EmptyFeatureVector(), AuthProof: &models.ChannelAuthProof{ NodeSig1Bytes: testSig.Serialize(), NodeSig2Bytes: testSig.Serialize(), @@ -839,6 +841,7 @@ func TestNotificationCancellation(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), ChannelPoint: *chanPoint, Capacity: chanValue, FundingScript: fn.Some(script), @@ -914,6 +917,7 @@ func TestChannelCloseNotification(t *testing.T) { BitcoinSig1Bytes: testSig.Serialize(), BitcoinSig2Bytes: testSig.Serialize(), }, + Features: lnwire.EmptyFeatureVector(), ChannelPoint: *chanUtxo, Capacity: chanValue, FundingScript: fn.Some(script), diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go index 571e0584d..83ee55db6 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -1,7 +1,6 @@ package netann import ( - "bytes" "errors" "fmt" @@ -49,14 +48,11 @@ func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, ChainHash: chanInfo.ChainHash, BitcoinKey1: chanInfo.BitcoinKey1Bytes, BitcoinKey2: chanInfo.BitcoinKey2Bytes, - Features: lnwire.NewRawFeatureVector(), + Features: chanInfo.Features.RawFeatureVector, ExtraOpaqueData: chanInfo.ExtraOpaqueData, } - err := chanAnn.Features.Decode(bytes.NewReader(chanInfo.Features)) - if err != nil { - return nil, nil, nil, err - } + var err error chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature( chanProof.BitcoinSig1Bytes, ) diff --git a/netann/channel_announcement_test.go b/netann/channel_announcement_test.go index e8c5799b4..fbd2b2d2b 100644 --- a/netann/channel_announcement_test.go +++ b/netann/channel_announcement_test.go @@ -24,11 +24,6 @@ func TestCreateChanAnnouncement(t *testing.T) { key := [33]byte{0x1} var sig lnwire.Sig features := lnwire.NewRawFeatureVector(lnwire.AnchorsRequired) - var featuresBuf bytes.Buffer - if err := features.Encode(&featuresBuf); err != nil { - t.Fatalf("unable to encode features: %v", err) - } - expChanAnn := &lnwire.ChannelAnnouncement1{ ChainHash: chainhash.Hash{0x1}, ShortChannelID: lnwire.ShortChannelID{BlockHeight: 1}, @@ -59,8 +54,10 @@ func TestCreateChanAnnouncement(t *testing.T) { NodeKey2Bytes: key, BitcoinKey1Bytes: key, BitcoinKey2Bytes: key, - Features: featuresBuf.Bytes(), - ExtraOpaqueData: expChanAnn.ExtraOpaqueData, + Features: lnwire.NewFeatureVector( + features, lnwire.Features, + ), + ExtraOpaqueData: expChanAnn.ExtraOpaqueData, } chanAnn, _, _, err := CreateChanAnnouncement( chanProof, chanInfo, nil, nil, diff --git a/routing/localchans/manager.go b/routing/localchans/manager.go index a75758e88..c76bad3a6 100644 --- a/routing/localchans/manager.go +++ b/routing/localchans/manager.go @@ -305,13 +305,6 @@ func (r *Manager) createEdge(channel *channeldb.OpenChannel, channelFlags = 1 } - var featureBuf bytes.Buffer - err := lnwire.NewRawFeatureVector().Encode(&featureBuf) - if err != nil { - return nil, nil, fmt.Errorf("unable to encode features: %w", - err) - } - // We need to make sure we use the real scid for public confirmed // zero-conf channels. shortChanID := channel.ShortChanID() @@ -323,7 +316,7 @@ func (r *Manager) createEdge(channel *channeldb.OpenChannel, info := &models.ChannelEdgeInfo{ ChannelID: shortChanID.ToUint64(), ChainHash: channel.ChainHash, - Features: featureBuf.Bytes(), + Features: lnwire.EmptyFeatureVector(), Capacity: channel.Capacity, ChannelPoint: channel.FundingOutpoint, } diff --git a/routing/localchans/manager_test.go b/routing/localchans/manager_test.go index 40da6fd04..d97b2552d 100644 --- a/routing/localchans/manager_test.go +++ b/routing/localchans/manager_test.go @@ -387,7 +387,7 @@ func TestCreateEdgeLower(t *testing.T) { expectedInfo := &models.ChannelEdgeInfo{ ChannelID: 8, ChainHash: channel.ChainHash, - Features: []byte{0, 0}, + Features: lnwire.EmptyFeatureVector(), Capacity: 9, ChannelPoint: channel.FundingOutpoint, NodeKey1Bytes: sp, @@ -475,7 +475,7 @@ func TestCreateEdgeHigher(t *testing.T) { expectedInfo := &models.ChannelEdgeInfo{ ChannelID: 8, ChainHash: channel.ChainHash, - Features: []byte{0, 0}, + Features: lnwire.EmptyFeatureVector(), Capacity: 9, ChannelPoint: channel.FundingOutpoint, NodeKey1Bytes: rp, diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index eea1bb22e..fd5f06656 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -348,6 +348,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) ( ChannelID: edge.ChannelID, AuthProof: &testAuthProof, ChannelPoint: fundingPoint, + Features: lnwire.EmptyFeatureVector(), Capacity: btcutil.Amount(edge.Capacity), } @@ -677,6 +678,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool, AuthProof: &testAuthProof, ChannelPoint: *fundingPoint, Capacity: testChannel.Capacity, + Features: lnwire.EmptyFeatureVector(), NodeKey1Bytes: node1Vertex, BitcoinKey1Bytes: node1Vertex, diff --git a/routing/router_test.go b/routing/router_test.go index d6e2fe52c..d149c423e 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -2741,6 +2741,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { NodeKey2Bytes: pub2, BitcoinKey1Bytes: pub1, BitcoinKey2Bytes: pub2, + Features: lnwire.EmptyFeatureVector(), AuthProof: nil, } require.NoError(t, ctx.graph.AddChannelEdge(ctxb, edge)) @@ -2816,6 +2817,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { edge = &models.ChannelEdgeInfo{ ChannelID: chanID.ToUint64(), + Features: lnwire.EmptyFeatureVector(), AuthProof: nil, } copy(edge.NodeKey1Bytes[:], node1Bytes)