graph/db: add version parameter to IsZombieEdge

Make IsZombieEdge version-aware and add corresponding method to
VersionedGraph. Convert TestEdgeInsertionDeletion to versioned test
using the createEdge helper for both v1 and v2 channel testing.
This commit is contained in:
Elle Mouton committed 2026-02-13 07:21:23 +02:00
1 parent d88fa01162
commit 03050a589d
5 files changed
+103 -65

No files matched your search

+8 -1
View File
@@ -735,7 +735,7 @@ func (c *ChannelGraph) ChannelView() ([]EdgePoint, error) {
func (c *ChannelGraph) IsZombieEdge(chanID uint64) (bool, [33]byte, [33]byte,
error) {
return c.db.IsZombieEdge(chanID)
return c.db.IsZombieEdge(lnwire.GossipVersion1, chanID)
}
// NumZombies returns the current number of zombie channels in the graph.
@@ -806,6 +806,13 @@ func (c *VersionedGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (
return c.db.FetchChannelEdgesByOutpoint(c.v, op)
}
// IsZombieEdge returns whether the edge is considered zombie for this version.
func (c *VersionedGraph) IsZombieEdge(chanID uint64) (bool, [33]byte,
[33]byte, error) {
return c.db.IsZombieEdge(c.v, chanID)
}
// AddrsForNode returns all known addresses for the target node public key.
func (c *VersionedGraph) AddrsForNode(ctx context.Context,
nodePub *btcec.PublicKey) (bool, []net.Addr, error) {
+78 -58
View File
@@ -142,6 +142,10 @@ var versionedTests = []versionedTest{
name: "add edge proof",
test: testAddEdgeProof,
},
{
name: "edge insertion deletion",
test: testEdgeInsertionDeletion,
},
{
name: "partial node",
test: testPartialNode,
@@ -549,88 +553,104 @@ func TestSetSourceNodeSameTimestamp(t *testing.T) {
require.Equal(t, testNode.LastUpdate, updatedNode.LastUpdate)
}
// TestEdgeInsertionDeletion tests the basic CRUD operations for channel edges.
func TestEdgeInsertionDeletion(t *testing.T) {
// testEdgeInsertionDeletion tests the basic CRUD operations for channel edges.
func testEdgeInsertionDeletion(t *testing.T, v lnwire.GossipVersion) {
t.Parallel()
ctx := t.Context()
graph := MakeTestGraph(t)
graph := NewVersionedGraph(MakeTestGraph(t), v)
// We'd like to test the insertion/deletion of edges, so we create two
// vertexes to connect.
node1 := createTestVertex(t, lnwire.GossipVersion1)
node2 := createTestVertex(t, lnwire.GossipVersion1)
node1 := createTestVertex(t, v)
node2 := createTestVertex(t, v)
// In addition to the fake vertexes we create some fake channel
// identifiers.
chanID := uint64(prand.Int63())
// Create a fake channel and add it to the graph.
const (
blockHeight = 1234
txIndex = 1
txPosition = 0
outPointIndex = 9
)
edgeInfo, shortChanID := createEdge(
v, blockHeight, txIndex, txPosition, outPointIndex, node1,
node2,
)
chanID := shortChanID.ToUint64()
outpoint := wire.OutPoint{
Hash: rev,
Index: 9,
Index: outPointIndex,
}
// Add the new edge to the database, this should proceed without any
// errors.
node1Pub, err := node1.PubKey()
require.NoError(t, err, "unable to generate node key")
node2Pub, err := node2.PubKey()
require.NoError(t, err, "unable to generate node key")
node1Vertex, err := route.NewVertexFromBytes(
node1Pub.SerializeCompressed(),
)
require.NoError(t, err)
node2Vertex, err := route.NewVertexFromBytes(
node2Pub.SerializeCompressed(),
)
require.NoError(t, err)
btcKey1, err := route.NewVertexFromBytes(
node1Pub.SerializeCompressed(),
)
require.NoError(t, err)
btcKey2, err := route.NewVertexFromBytes(
node2Pub.SerializeCompressed(),
)
require.NoError(t, err)
proof := models.NewV1ChannelAuthProof(
testSig.Serialize(),
testSig.Serialize(),
testSig.Serialize(),
testSig.Serialize(),
)
edgeInfo, err := models.NewV1Channel(
chanID, *chaincfg.MainNetParams.GenesisHash, node1Vertex,
node2Vertex, &models.ChannelV1Fields{
BitcoinKey1Bytes: btcKey1,
BitcoinKey2Bytes: btcKey2,
},
models.WithChanProof(proof),
models.WithChannelPoint(outpoint),
models.WithCapacity(9000),
)
require.NoError(t, err)
require.NoError(t, graph.AddChannelEdge(ctx, edgeInfo))
assertEdgeWithNoPoliciesInCache(t, graph, edgeInfo)
assertEdgeWithNoPoliciesInCache(t, graph.ChannelGraph, edgeInfo)
// Show that trying to insert the same channel again will return the
// expected error.
err = graph.AddChannelEdge(ctx, edgeInfo)
err := graph.AddChannelEdge(ctx, edgeInfo)
require.ErrorIs(t, err, ErrEdgeAlreadyExist)
// Ensure that both policies are returned as unknown (nil).
_, e1, e2, err := graph.FetchChannelEdgesByID(chanID)
// Ensure that both policies are returned as unknown (nil) and that
// the edge info round-trips correctly.
dbEdge, e1, e2, err := graph.FetchChannelEdgesByID(chanID)
require.NoError(t, err)
require.Nil(t, e1)
require.Nil(t, e2)
// Verify core fields match.
require.Equal(t, edgeInfo.ChannelID, dbEdge.ChannelID)
require.Equal(t, edgeInfo.Version, dbEdge.Version)
require.Equal(t, edgeInfo.NodeKey1Bytes, dbEdge.NodeKey1Bytes)
require.Equal(t, edgeInfo.NodeKey2Bytes, dbEdge.NodeKey2Bytes)
require.Equal(t, edgeInfo.ChainHash, dbEdge.ChainHash)
require.Equal(t, edgeInfo.ChannelPoint, dbEdge.ChannelPoint)
require.Equal(t, edgeInfo.Capacity, dbEdge.Capacity)
// Verify auth proof round-trips.
require.NotNil(t, dbEdge.AuthProof)
require.Equal(t, edgeInfo.AuthProof.Version, dbEdge.AuthProof.Version)
// Verify version-specific fields.
switch v {
case lnwire.GossipVersion1:
require.Equal(t,
edgeInfo.BitcoinKey1Bytes, dbEdge.BitcoinKey1Bytes,
)
require.Equal(t,
edgeInfo.BitcoinKey2Bytes, dbEdge.BitcoinKey2Bytes,
)
require.Equal(t,
edgeInfo.ExtraOpaqueData, dbEdge.ExtraOpaqueData,
)
case lnwire.GossipVersion2:
require.Equal(t,
edgeInfo.BitcoinKey1Bytes, dbEdge.BitcoinKey1Bytes,
)
require.Equal(t,
edgeInfo.BitcoinKey2Bytes, dbEdge.BitcoinKey2Bytes,
)
require.Equal(t,
edgeInfo.MerkleRootHash, dbEdge.MerkleRootHash,
)
require.Equal(t,
edgeInfo.FundingScript, dbEdge.FundingScript,
)
require.Equal(t,
edgeInfo.ExtraSignedFields, dbEdge.ExtraSignedFields,
)
}
// Also verify fetching by outpoint returns the same data.
dbEdge2, _, _, err := graph.FetchChannelEdgesByOutpoint(&outpoint)
require.NoError(t, err)
require.Equal(t, dbEdge.ChannelID, dbEdge2.ChannelID)
// Next, attempt to delete the edge from the database, again this
// should proceed without any issues.
require.NoError(t, graph.DeleteChannelEdges(false, true, chanID))
assertNoEdge(t, graph, chanID)
assertNoEdge(t, graph.ChannelGraph, chanID)
// Ensure that any query attempts to lookup the delete channel edge are
// properly deleted.
+2 -1
View File
@@ -314,7 +314,8 @@ type Store interface { //nolint:interfacebloat
// IsZombieEdge returns whether the edge is considered zombie. If it is
// a zombie, then the two node public keys corresponding to this edge
// are also returned.
IsZombieEdge(chanID uint64) (bool, [33]byte, [33]byte, error)
IsZombieEdge(v lnwire.GossipVersion, chanID uint64) (bool, [33]byte,
[33]byte, error)
// NumZombies returns the current number of zombie channels in the
// graph.
+7 -2
View File
@@ -4257,14 +4257,19 @@ func (c *KVStore) markEdgeLiveUnsafe(tx kvdb.RwTx, chanID uint64) error {
// IsZombieEdge returns whether the edge is considered zombie. If it is a
// zombie, then the two node public keys corresponding to this edge are also
// returned.
func (c *KVStore) IsZombieEdge(chanID uint64) (bool, [33]byte, [33]byte,
error) {
func (c *KVStore) IsZombieEdge(v lnwire.GossipVersion,
chanID uint64) (bool, [33]byte, [33]byte, error) {
var (
isZombie bool
pubKey1, pubKey2 [33]byte
)
if v != lnwire.GossipVersion1 {
return false, [33]byte{}, [33]byte{},
ErrVersionNotSupportedForKVDB
}
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
edges := tx.ReadBucket(edgeBucket)
if edges == nil {
+8 -3
View File
@@ -1824,8 +1824,8 @@ func (s *SQLStore) MarkEdgeLive(chanID uint64) error {
// returned.
//
// NOTE: part of the Store interface.
func (s *SQLStore) IsZombieEdge(chanID uint64) (bool, [33]byte, [33]byte,
error) {
func (s *SQLStore) IsZombieEdge(v lnwire.GossipVersion,
chanID uint64) (bool, [33]byte, [33]byte, error) {
var (
ctx = context.TODO()
@@ -1834,11 +1834,16 @@ func (s *SQLStore) IsZombieEdge(chanID uint64) (bool, [33]byte, [33]byte,
chanIDB = channelIDToBytes(chanID)
)
if !isKnownGossipVersion(v) {
return false, [33]byte{}, [33]byte{},
fmt.Errorf("unsupported gossip version: %d", v)
}
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
zombie, err := db.GetZombieChannel(
ctx, sqlc.GetZombieChannelParams{
Scid: chanIDB,
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
},
)
if errors.Is(err, sql.ErrNoRows) {