graph/db+sqldb: impl IsPublicNode
Which lets us run `TestNodeIsPublic` against our SQL DB backends. Note that we need to tweak the tests a little bit so that `AddLightningNode` for the same node is always called with a newer LastUpdate time else it will fail the SQL constraint that only allows the upsert if the update is newer than the persisted one.
This commit is contained in:
5 files changed
+90
-9
No files matched your search
+21
-9
@@ -73,13 +73,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func createLightningNode(priv *btcec.PrivateKey) *models.LightningNode {
|
func createLightningNode(priv *btcec.PrivateKey) *models.LightningNode {
|
||||||
updateTime := prand.Int63()
|
|
||||||
|
|
||||||
pub := priv.PubKey().SerializeCompressed()
|
pub := priv.PubKey().SerializeCompressed()
|
||||||
n := &models.LightningNode{
|
n := &models.LightningNode{
|
||||||
HaveNodeAnnouncement: true,
|
HaveNodeAnnouncement: true,
|
||||||
AuthSigBytes: testSig.Serialize(),
|
AuthSigBytes: testSig.Serialize(),
|
||||||
LastUpdate: time.Unix(updateTime, 0),
|
LastUpdate: time.Unix(nextUpdateTime(), 0),
|
||||||
Color: color.RGBA{1, 2, 3, 0},
|
Color: color.RGBA{1, 2, 3, 0},
|
||||||
Alias: "kek" + hex.EncodeToString(pub),
|
Alias: "kek" + hex.EncodeToString(pub),
|
||||||
Features: testFeatures,
|
Features: testFeatures,
|
||||||
@@ -3439,6 +3437,20 @@ func TestNodePruningUpdateIndexDeletion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
updateTime = prand.Int63()
|
||||||
|
updateTimeMu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func nextUpdateTime() int64 {
|
||||||
|
updateTimeMu.Lock()
|
||||||
|
defer updateTimeMu.Unlock()
|
||||||
|
|
||||||
|
updateTime++
|
||||||
|
|
||||||
|
return updateTime
|
||||||
|
}
|
||||||
|
|
||||||
// TestNodeIsPublic ensures that we properly detect nodes that are seen as
|
// TestNodeIsPublic ensures that we properly detect nodes that are seen as
|
||||||
// public within the network graph.
|
// public within the network graph.
|
||||||
func TestNodeIsPublic(t *testing.T) {
|
func TestNodeIsPublic(t *testing.T) {
|
||||||
@@ -3453,19 +3465,19 @@ func TestNodeIsPublic(t *testing.T) {
|
|||||||
// We'll need to create a separate database and channel graph for each
|
// We'll need to create a separate database and channel graph for each
|
||||||
// participant to replicate real-world scenarios (private edges being in
|
// participant to replicate real-world scenarios (private edges being in
|
||||||
// some graphs but not others, etc.).
|
// some graphs but not others, etc.).
|
||||||
aliceGraph := MakeTestGraph(t)
|
aliceGraph := MakeTestGraphNew(t)
|
||||||
aliceNode := createTestVertex(t)
|
aliceNode := createTestVertex(t)
|
||||||
if err := aliceGraph.SetSourceNode(ctx, aliceNode); err != nil {
|
if err := aliceGraph.SetSourceNode(ctx, aliceNode); err != nil {
|
||||||
t.Fatalf("unable to set source node: %v", err)
|
t.Fatalf("unable to set source node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bobGraph := MakeTestGraph(t)
|
bobGraph := MakeTestGraphNew(t)
|
||||||
bobNode := createTestVertex(t)
|
bobNode := createTestVertex(t)
|
||||||
if err := bobGraph.SetSourceNode(ctx, bobNode); err != nil {
|
if err := bobGraph.SetSourceNode(ctx, bobNode); err != nil {
|
||||||
t.Fatalf("unable to set source node: %v", err)
|
t.Fatalf("unable to set source node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
carolGraph := MakeTestGraph(t)
|
carolGraph := MakeTestGraphNew(t)
|
||||||
carolNode := createTestVertex(t)
|
carolNode := createTestVertex(t)
|
||||||
if err := carolGraph.SetSourceNode(ctx, carolNode); err != nil {
|
if err := carolGraph.SetSourceNode(ctx, carolNode); err != nil {
|
||||||
t.Fatalf("unable to set source node: %v", err)
|
t.Fatalf("unable to set source node: %v", err)
|
||||||
@@ -3481,13 +3493,13 @@ func TestNodeIsPublic(t *testing.T) {
|
|||||||
graphs := []*ChannelGraph{aliceGraph, bobGraph, carolGraph}
|
graphs := []*ChannelGraph{aliceGraph, bobGraph, carolGraph}
|
||||||
for _, graph := range graphs {
|
for _, graph := range graphs {
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
|
node.LastUpdate = time.Unix(nextUpdateTime(), 0)
|
||||||
err := graph.AddLightningNode(ctx, node)
|
err := graph.AddLightningNode(ctx, node)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
for _, edge := range edges {
|
for _, edge := range edges {
|
||||||
if err := graph.AddChannelEdge(ctx, edge); err != nil {
|
err := graph.AddChannelEdge(ctx, edge)
|
||||||
t.Fatalf("unable to add edge: %v", err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ type SQLQueries interface {
|
|||||||
GetNodesByLastUpdateRange(ctx context.Context, arg sqlc.GetNodesByLastUpdateRangeParams) ([]sqlc.Node, error)
|
GetNodesByLastUpdateRange(ctx context.Context, arg sqlc.GetNodesByLastUpdateRangeParams) ([]sqlc.Node, error)
|
||||||
ListNodesPaginated(ctx context.Context, arg sqlc.ListNodesPaginatedParams) ([]sqlc.Node, error)
|
ListNodesPaginated(ctx context.Context, arg sqlc.ListNodesPaginatedParams) ([]sqlc.Node, error)
|
||||||
ListNodeIDsAndPubKeys(ctx context.Context, arg sqlc.ListNodeIDsAndPubKeysParams) ([]sqlc.ListNodeIDsAndPubKeysRow, error)
|
ListNodeIDsAndPubKeys(ctx context.Context, arg sqlc.ListNodeIDsAndPubKeysParams) ([]sqlc.ListNodeIDsAndPubKeysRow, error)
|
||||||
|
IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error)
|
||||||
DeleteNodeByPubKey(ctx context.Context, arg sqlc.DeleteNodeByPubKeyParams) (sql.Result, error)
|
DeleteNodeByPubKey(ctx context.Context, arg sqlc.DeleteNodeByPubKeyParams) (sql.Result, error)
|
||||||
|
|
||||||
GetExtraNodeTypes(ctx context.Context, nodeID int64) ([]sqlc.NodeExtraType, error)
|
GetExtraNodeTypes(ctx context.Context, nodeID int64) ([]sqlc.NodeExtraType, error)
|
||||||
@@ -1990,6 +1991,29 @@ func (s *SQLStore) ChannelID(chanPoint *wire.OutPoint) (uint64, error) {
|
|||||||
return channelID, nil
|
return channelID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPublicNode is a helper method that determines whether the node with the
|
||||||
|
// given public key is seen as a public node in the graph from the graph's
|
||||||
|
// source node's point of view.
|
||||||
|
//
|
||||||
|
// NOTE: part of the V1Store interface.
|
||||||
|
func (s *SQLStore) IsPublicNode(pubKey [33]byte) (bool, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
var isPublic bool
|
||||||
|
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
|
||||||
|
var err error
|
||||||
|
isPublic, err = db.IsPublicV1Node(ctx, pubKey[:])
|
||||||
|
|
||||||
|
return err
|
||||||
|
}, sqldb.NoOpReset)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("unable to check if node is "+
|
||||||
|
"public: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPublic, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FetchChanInfos returns the set of channel edges that correspond to the passed
|
// FetchChanInfos returns the set of channel edges that correspond to the passed
|
||||||
// channel ID's. If an edge is the query is unknown to the database, it will
|
// channel ID's. If an edge is the query is unknown to the database, it will
|
||||||
// skipped and the result will contain only those edges that exist at the time
|
// skipped and the result will contain only those edges that exist at the time
|
||||||
|
|||||||
@@ -1360,6 +1360,32 @@ func (q *Queries) InsertNodeFeature(ctx context.Context, arg InsertNodeFeaturePa
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isPublicV1Node = `-- name: IsPublicV1Node :one
|
||||||
|
SELECT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM channels c
|
||||||
|
JOIN nodes n ON n.id = c.node_id_1 OR n.id = c.node_id_2
|
||||||
|
-- NOTE: we hard-code the version here since the clauses
|
||||||
|
-- here that determine if a node is public is specific
|
||||||
|
-- to the V1 gossip protocol. In V1, a node is public
|
||||||
|
-- if it has a public channel and a public channel is one
|
||||||
|
-- where we have the set of signatures of the channel
|
||||||
|
-- announcement. It is enough to just check that we have
|
||||||
|
-- one of the signatures since we only ever set them
|
||||||
|
-- together.
|
||||||
|
WHERE c.version = 1
|
||||||
|
AND c.bitcoin_1_signature IS NOT NULL
|
||||||
|
AND n.pub_key = $1
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, isPublicV1Node, pubKey)
|
||||||
|
var exists bool
|
||||||
|
err := row.Scan(&exists)
|
||||||
|
return exists, err
|
||||||
|
}
|
||||||
|
|
||||||
const isZombieChannel = `-- name: IsZombieChannel :one
|
const isZombieChannel = `-- name: IsZombieChannel :one
|
||||||
SELECT EXISTS (
|
SELECT EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ type Querier interface {
|
|||||||
InsertMigratedInvoice(ctx context.Context, arg InsertMigratedInvoiceParams) (int64, error)
|
InsertMigratedInvoice(ctx context.Context, arg InsertMigratedInvoiceParams) (int64, error)
|
||||||
InsertNodeAddress(ctx context.Context, arg InsertNodeAddressParams) error
|
InsertNodeAddress(ctx context.Context, arg InsertNodeAddressParams) error
|
||||||
InsertNodeFeature(ctx context.Context, arg InsertNodeFeatureParams) error
|
InsertNodeFeature(ctx context.Context, arg InsertNodeFeatureParams) error
|
||||||
|
IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error)
|
||||||
IsZombieChannel(ctx context.Context, arg IsZombieChannelParams) (bool, error)
|
IsZombieChannel(ctx context.Context, arg IsZombieChannelParams) (bool, error)
|
||||||
ListChannelsByNodeID(ctx context.Context, arg ListChannelsByNodeIDParams) ([]ListChannelsByNodeIDRow, error)
|
ListChannelsByNodeID(ctx context.Context, arg ListChannelsByNodeIDParams) ([]ListChannelsByNodeIDRow, error)
|
||||||
ListChannelsWithPoliciesPaginated(ctx context.Context, arg ListChannelsWithPoliciesPaginatedParams) ([]ListChannelsWithPoliciesPaginatedRow, error)
|
ListChannelsWithPoliciesPaginated(ctx context.Context, arg ListChannelsWithPoliciesPaginatedParams) ([]ListChannelsWithPoliciesPaginatedRow, error)
|
||||||
|
|||||||
@@ -47,6 +47,24 @@ WHERE version = $1 AND id > $2
|
|||||||
ORDER BY id
|
ORDER BY id
|
||||||
LIMIT $3;
|
LIMIT $3;
|
||||||
|
|
||||||
|
-- name: IsPublicV1Node :one
|
||||||
|
SELECT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM channels c
|
||||||
|
JOIN nodes n ON n.id = c.node_id_1 OR n.id = c.node_id_2
|
||||||
|
-- NOTE: we hard-code the version here since the clauses
|
||||||
|
-- here that determine if a node is public is specific
|
||||||
|
-- to the V1 gossip protocol. In V1, a node is public
|
||||||
|
-- if it has a public channel and a public channel is one
|
||||||
|
-- where we have the set of signatures of the channel
|
||||||
|
-- announcement. It is enough to just check that we have
|
||||||
|
-- one of the signatures since we only ever set them
|
||||||
|
-- together.
|
||||||
|
WHERE c.version = 1
|
||||||
|
AND c.bitcoin_1_signature IS NOT NULL
|
||||||
|
AND n.pub_key = $1
|
||||||
|
);
|
||||||
|
|
||||||
-- name: DeleteNodeByPubKey :execresult
|
-- name: DeleteNodeByPubKey :execresult
|
||||||
DELETE FROM nodes
|
DELETE FROM nodes
|
||||||
WHERE pub_key = $1
|
WHERE pub_key = $1
|
||||||
|
|||||||
Reference in new issue
Block a user