graph/db: treat empty channel signatures as missing
This commit improves handling of missing channel signatures in the database: - Return nil from auth proof accessors instead of empty slices so that missing signatures are stored as NULL in SQL. - Update public channel checks to require signature length > 0, which properly handles existing empty bytea values in the database. - Add regression test covering empty v1 and v2 channel signatures to prevent future issues.
This commit is contained in:
5 files changed
+85
-27
No files matched your search
@@ -154,6 +154,10 @@ var versionedTests = []versionedTest{
|
|||||||
name: "node is public",
|
name: "node is public",
|
||||||
test: testNodeIsPublic,
|
test: testNodeIsPublic,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "node is public empty channel signature",
|
||||||
|
test: testIsPublicNodeEmptyChannelSignature,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestVersionedDBs runs various tests against both v1 and v2 versioned
|
// TestVersionedDBs runs various tests against both v1 and v2 versioned
|
||||||
@@ -4265,6 +4269,59 @@ func testNodeIsPublic(t *testing.T, v lnwire.GossipVersion) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testIsPublicNodeEmptyChannelSignature ensures empty channel signatures don't
|
||||||
|
// mark nodes as public.
|
||||||
|
func testIsPublicNodeEmptyChannelSignature(t *testing.T,
|
||||||
|
v lnwire.GossipVersion) {
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
ctx := t.Context()
|
||||||
|
|
||||||
|
testGraph := MakeTestGraph(t)
|
||||||
|
graph := NewVersionedGraph(testGraph, v)
|
||||||
|
|
||||||
|
// Set a source node as it's required for IsPublicNode.
|
||||||
|
sourceNode := createTestVertex(t, v)
|
||||||
|
err := graph.SetSourceNode(ctx, sourceNode)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
node1 := createTestVertex(t, v)
|
||||||
|
|
||||||
|
node1.LastUpdate = nextUpdateTime()
|
||||||
|
|
||||||
|
err = graph.AddNode(ctx, node1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create an edge between source node and node1, with
|
||||||
|
// empty signatures. This tests that empty signatures
|
||||||
|
// don't mark nodes as public.
|
||||||
|
edgeInfo, _ := createEdge(
|
||||||
|
v, 10, 0, 0, 0, sourceNode, node1,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
|
switch v {
|
||||||
|
case lnwire.GossipVersion1:
|
||||||
|
edgeInfo.AuthProof =
|
||||||
|
models.NewV1ChannelAuthProof(
|
||||||
|
[]byte{}, []byte{},
|
||||||
|
[]byte{}, []byte{},
|
||||||
|
)
|
||||||
|
case lnwire.GossipVersion2:
|
||||||
|
edgeInfo.AuthProof =
|
||||||
|
models.NewV2ChannelAuthProof([]byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = graph.AddChannelEdge(ctx, edgeInfo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// node1 should NOT be considered public because the
|
||||||
|
// channel announcement has empty signatures.
|
||||||
|
isPublic, err := graph.IsPublicNode(node1.PubKeyBytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, isPublic)
|
||||||
|
}
|
||||||
|
|
||||||
// BenchmarkIsPublicNode measures the performance of IsPublicNode when checking
|
// BenchmarkIsPublicNode measures the performance of IsPublicNode when checking
|
||||||
// a large number of nodes.
|
// a large number of nodes.
|
||||||
func BenchmarkIsPublicNode(b *testing.B) {
|
func BenchmarkIsPublicNode(b *testing.B) {
|
||||||
|
|||||||
@@ -3377,8 +3377,11 @@ func (c *KVStore) isPublic(tx kvdb.RTx, nodePub route.Vertex,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Since the edge _does_ extend to the source node, we'll also
|
// Since the edge _does_ extend to the source node, we'll also
|
||||||
// need to ensure that this is a public edge.
|
// need to ensure that this is a public edge with valid
|
||||||
if info.AuthProof != nil {
|
// signatures (not empty).
|
||||||
|
if info.AuthProof != nil && !info.AuthProof.IsEmpty() &&
|
||||||
|
len(info.AuthProof.BitcoinSig1()) > 0 {
|
||||||
|
|
||||||
nodeIsPublic = true
|
nodeIsPublic = true
|
||||||
return errDone
|
return errDone
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,31 +89,29 @@ func NewV2ChannelAuthProof(signature []byte) *ChannelAuthProof {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeSig1 returns the first node signature bytes, or an empty slice if not
|
// NodeSig1 returns the first node signature bytes, or nil if not present.
|
||||||
// present.
|
|
||||||
func (c *ChannelAuthProof) NodeSig1() []byte {
|
func (c *ChannelAuthProof) NodeSig1() []byte {
|
||||||
return c.NodeSig1Bytes.UnwrapOr([]byte{})
|
return c.NodeSig1Bytes.UnwrapOr(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeSig2 returns the second node signature bytes, or an empty slice if not
|
// NodeSig2 returns the second node signature bytes, or nil if not present.
|
||||||
// present.
|
|
||||||
func (c *ChannelAuthProof) NodeSig2() []byte {
|
func (c *ChannelAuthProof) NodeSig2() []byte {
|
||||||
return c.NodeSig2Bytes.UnwrapOr([]byte{})
|
return c.NodeSig2Bytes.UnwrapOr(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BitcoinSig1 returns the first bitcoin signature bytes, or an empty slice if
|
// BitcoinSig1 returns the first bitcoin signature bytes, or nil if not
|
||||||
// not present.
|
// present.
|
||||||
func (c *ChannelAuthProof) BitcoinSig1() []byte {
|
func (c *ChannelAuthProof) BitcoinSig1() []byte {
|
||||||
return c.BitcoinSig1Bytes.UnwrapOr([]byte{})
|
return c.BitcoinSig1Bytes.UnwrapOr(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BitcoinSig2 returns the second bitcoin signature bytes, or an empty slice if
|
// BitcoinSig2 returns the second bitcoin signature bytes, or nil if not
|
||||||
// not present.
|
// present.
|
||||||
func (c *ChannelAuthProof) BitcoinSig2() []byte {
|
func (c *ChannelAuthProof) BitcoinSig2() []byte {
|
||||||
return c.BitcoinSig2Bytes.UnwrapOr([]byte{})
|
return c.BitcoinSig2Bytes.UnwrapOr(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sig returns the v2 signature bytes, or an empty slice if not present.
|
// Sig returns the v2 signature bytes, or nil if not present.
|
||||||
func (c *ChannelAuthProof) Sig() []byte {
|
func (c *ChannelAuthProof) Sig() []byte {
|
||||||
return c.Signature.UnwrapOr([]byte{})
|
return c.Signature.UnwrapOr(nil)
|
||||||
}
|
}
|
||||||
@@ -2085,7 +2085,7 @@ WHERE last_update >= $1
|
|||||||
SELECT 1
|
SELECT 1
|
||||||
FROM graph_channels c
|
FROM graph_channels c
|
||||||
WHERE c.version = 1
|
WHERE c.version = 1
|
||||||
AND c.bitcoin_1_signature IS NOT NULL
|
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||||
AND (c.node_id_1 = graph_nodes.id OR c.node_id_2 = graph_nodes.id)
|
AND (c.node_id_1 = graph_nodes.id OR c.node_id_2 = graph_nodes.id)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -2211,7 +2211,7 @@ func (q *Queries) GetPruneTip(ctx context.Context) (GraphPruneLog, error) {
|
|||||||
const getPublicV1ChannelsBySCID = `-- name: GetPublicV1ChannelsBySCID :many
|
const getPublicV1ChannelsBySCID = `-- name: GetPublicV1ChannelsBySCID :many
|
||||||
SELECT id, version, scid, node_id_1, node_id_2, outpoint, capacity, bitcoin_key_1, bitcoin_key_2, node_1_signature, node_2_signature, bitcoin_1_signature, bitcoin_2_signature, signature, funding_pk_script, merkle_root_hash
|
SELECT id, version, scid, node_id_1, node_id_2, outpoint, capacity, bitcoin_key_1, bitcoin_key_2, node_1_signature, node_2_signature, bitcoin_1_signature, bitcoin_2_signature, signature, funding_pk_script, merkle_root_hash
|
||||||
FROM graph_channels
|
FROM graph_channels
|
||||||
WHERE node_1_signature IS NOT NULL
|
WHERE COALESCE(length(node_1_signature), 0) > 0
|
||||||
AND scid >= $1
|
AND scid >= $1
|
||||||
AND scid < $2
|
AND scid < $2
|
||||||
`
|
`
|
||||||
@@ -2732,14 +2732,14 @@ SELECT EXISTS (
|
|||||||
-- one of the signatures since we only ever set them
|
-- one of the signatures since we only ever set them
|
||||||
-- together.
|
-- together.
|
||||||
WHERE c.version = 1
|
WHERE c.version = 1
|
||||||
AND c.bitcoin_1_signature IS NOT NULL
|
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||||
AND n.pub_key = $1
|
AND n.pub_key = $1
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM graph_channels c
|
FROM graph_channels c
|
||||||
JOIN graph_nodes n ON n.id = c.node_id_2
|
JOIN graph_nodes n ON n.id = c.node_id_2
|
||||||
WHERE c.version = 1
|
WHERE c.version = 1
|
||||||
AND c.bitcoin_1_signature IS NOT NULL
|
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||||
AND n.pub_key = $1
|
AND n.pub_key = $1
|
||||||
)
|
)
|
||||||
`
|
`
|
||||||
@@ -2760,7 +2760,7 @@ SELECT EXISTS (
|
|||||||
-- here that determine if a node is public is specific
|
-- here that determine if a node is public is specific
|
||||||
-- to the V2 gossip protocol.
|
-- to the V2 gossip protocol.
|
||||||
WHERE c.version = 2
|
WHERE c.version = 2
|
||||||
AND c.signature IS NOT NULL
|
AND COALESCE(length(c.signature), 0) > 0
|
||||||
AND n.pub_key = $1
|
AND n.pub_key = $1
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|||||||
@@ -101,14 +101,14 @@ SELECT EXISTS (
|
|||||||
-- one of the signatures since we only ever set them
|
-- one of the signatures since we only ever set them
|
||||||
-- together.
|
-- together.
|
||||||
WHERE c.version = 1
|
WHERE c.version = 1
|
||||||
AND c.bitcoin_1_signature IS NOT NULL
|
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||||
AND n.pub_key = $1
|
AND n.pub_key = $1
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM graph_channels c
|
FROM graph_channels c
|
||||||
JOIN graph_nodes n ON n.id = c.node_id_2
|
JOIN graph_nodes n ON n.id = c.node_id_2
|
||||||
WHERE c.version = 1
|
WHERE c.version = 1
|
||||||
AND c.bitcoin_1_signature IS NOT NULL
|
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||||
AND n.pub_key = $1
|
AND n.pub_key = $1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ SELECT EXISTS (
|
|||||||
-- here that determine if a node is public is specific
|
-- here that determine if a node is public is specific
|
||||||
-- to the V2 gossip protocol.
|
-- to the V2 gossip protocol.
|
||||||
WHERE c.version = 2
|
WHERE c.version = 2
|
||||||
AND c.signature IS NOT NULL
|
AND COALESCE(length(c.signature), 0) > 0
|
||||||
AND n.pub_key = $1
|
AND n.pub_key = $1
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
@@ -251,7 +251,7 @@ WHERE last_update >= @start_time
|
|||||||
SELECT 1
|
SELECT 1
|
||||||
FROM graph_channels c
|
FROM graph_channels c
|
||||||
WHERE c.version = 1
|
WHERE c.version = 1
|
||||||
AND c.bitcoin_1_signature IS NOT NULL
|
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||||
AND (c.node_id_1 = graph_nodes.id OR c.node_id_2 = graph_nodes.id)
|
AND (c.node_id_1 = graph_nodes.id OR c.node_id_2 = graph_nodes.id)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -731,7 +731,7 @@ WHERE c.version = $1
|
|||||||
-- name: GetPublicV1ChannelsBySCID :many
|
-- name: GetPublicV1ChannelsBySCID :many
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM graph_channels
|
FROM graph_channels
|
||||||
WHERE node_1_signature IS NOT NULL
|
WHERE COALESCE(length(node_1_signature), 0) > 0
|
||||||
AND scid >= @start_scid
|
AND scid >= @start_scid
|
||||||
AND scid < @end_scid;
|
AND scid < @end_scid;
|
||||||
|
|
||||||
@@ -1217,4 +1217,4 @@ ON CONFLICT (channel_id, node_id, version)
|
|||||||
message_flags = EXCLUDED.message_flags,
|
message_flags = EXCLUDED.message_flags,
|
||||||
channel_flags = EXCLUDED.channel_flags,
|
channel_flags = EXCLUDED.channel_flags,
|
||||||
signature = EXCLUDED.signature
|
signature = EXCLUDED.signature
|
||||||
RETURNING id;
|
RETURNING id;
|
||||||
Reference in new issue
Block a user