mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-12-04 18:01:57 +01:00
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:
@@ -73,13 +73,11 @@ var (
|
||||
)
|
||||
|
||||
func createLightningNode(priv *btcec.PrivateKey) *models.LightningNode {
|
||||
updateTime := prand.Int63()
|
||||
|
||||
pub := priv.PubKey().SerializeCompressed()
|
||||
n := &models.LightningNode{
|
||||
HaveNodeAnnouncement: true,
|
||||
AuthSigBytes: testSig.Serialize(),
|
||||
LastUpdate: time.Unix(updateTime, 0),
|
||||
LastUpdate: time.Unix(nextUpdateTime(), 0),
|
||||
Color: color.RGBA{1, 2, 3, 0},
|
||||
Alias: "kek" + hex.EncodeToString(pub),
|
||||
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
|
||||
// public within the network graph.
|
||||
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
|
||||
// participant to replicate real-world scenarios (private edges being in
|
||||
// some graphs but not others, etc.).
|
||||
aliceGraph := MakeTestGraph(t)
|
||||
aliceGraph := MakeTestGraphNew(t)
|
||||
aliceNode := createTestVertex(t)
|
||||
if err := aliceGraph.SetSourceNode(ctx, aliceNode); err != nil {
|
||||
t.Fatalf("unable to set source node: %v", err)
|
||||
}
|
||||
|
||||
bobGraph := MakeTestGraph(t)
|
||||
bobGraph := MakeTestGraphNew(t)
|
||||
bobNode := createTestVertex(t)
|
||||
if err := bobGraph.SetSourceNode(ctx, bobNode); err != nil {
|
||||
t.Fatalf("unable to set source node: %v", err)
|
||||
}
|
||||
|
||||
carolGraph := MakeTestGraph(t)
|
||||
carolGraph := MakeTestGraphNew(t)
|
||||
carolNode := createTestVertex(t)
|
||||
if err := carolGraph.SetSourceNode(ctx, carolNode); err != nil {
|
||||
t.Fatalf("unable to set source node: %v", err)
|
||||
@@ -3481,13 +3493,13 @@ func TestNodeIsPublic(t *testing.T) {
|
||||
graphs := []*ChannelGraph{aliceGraph, bobGraph, carolGraph}
|
||||
for _, graph := range graphs {
|
||||
for _, node := range nodes {
|
||||
node.LastUpdate = time.Unix(nextUpdateTime(), 0)
|
||||
err := graph.AddLightningNode(ctx, node)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
for _, edge := range edges {
|
||||
if err := graph.AddChannelEdge(ctx, edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
err := graph.AddChannelEdge(ctx, edge)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ type SQLQueries interface {
|
||||
GetNodesByLastUpdateRange(ctx context.Context, arg sqlc.GetNodesByLastUpdateRangeParams) ([]sqlc.Node, error)
|
||||
ListNodesPaginated(ctx context.Context, arg sqlc.ListNodesPaginatedParams) ([]sqlc.Node, 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)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@@ -1360,6 +1360,32 @@ func (q *Queries) InsertNodeFeature(ctx context.Context, arg InsertNodeFeaturePa
|
||||
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
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
|
||||
@@ -74,6 +74,7 @@ type Querier interface {
|
||||
InsertMigratedInvoice(ctx context.Context, arg InsertMigratedInvoiceParams) (int64, error)
|
||||
InsertNodeAddress(ctx context.Context, arg InsertNodeAddressParams) error
|
||||
InsertNodeFeature(ctx context.Context, arg InsertNodeFeatureParams) error
|
||||
IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error)
|
||||
IsZombieChannel(ctx context.Context, arg IsZombieChannelParams) (bool, error)
|
||||
ListChannelsByNodeID(ctx context.Context, arg ListChannelsByNodeIDParams) ([]ListChannelsByNodeIDRow, error)
|
||||
ListChannelsWithPoliciesPaginated(ctx context.Context, arg ListChannelsWithPoliciesPaginatedParams) ([]ListChannelsWithPoliciesPaginatedRow, error)
|
||||
|
||||
@@ -47,6 +47,24 @@ WHERE version = $1 AND id > $2
|
||||
ORDER BY id
|
||||
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
|
||||
DELETE FROM nodes
|
||||
WHERE pub_key = $1
|
||||
|
||||
Reference in New Issue
Block a user