From b61b1053f08256dfd2e069de3a6950a2a561e059 Mon Sep 17 00:00:00 2001 From: Chen Kai <281165273grape@gmail.com> Date: Mon, 3 Feb 2025 11:59:26 +0800 Subject: [PATCH] feat:expose discv5 function for portal discv5 json rpc Signed-off-by: Chen Kai <281165273grape@gmail.com> --- cmd/devp2p/discv4cmd.go | 2 +- cmd/devp2p/discv5cmd.go | 2 +- p2p/discover/table.go | 46 ++++++++++++++++++ p2p/discover/v4_udp.go | 4 +- p2p/discover/v5_udp.go | 93 ++++++++++++++++++++++++++++--------- p2p/discover/v5_udp_test.go | 4 +- 6 files changed, 124 insertions(+), 27 deletions(-) diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go index 8c48b3a557..0c832262a6 100644 --- a/cmd/devp2p/discv4cmd.go +++ b/cmd/devp2p/discv4cmd.go @@ -163,7 +163,7 @@ func discv4Ping(ctx *cli.Context) error { defer disc.Close() start := time.Now() - if err := disc.Ping(n); err != nil { + if err := disc.PingWithoutResp(n); err != nil { return fmt.Errorf("node didn't respond: %v", err) } fmt.Printf("node responded to ping (RTT %v).\n", time.Since(start)) diff --git a/cmd/devp2p/discv5cmd.go b/cmd/devp2p/discv5cmd.go index 2422ef6644..b8a02b560a 100644 --- a/cmd/devp2p/discv5cmd.go +++ b/cmd/devp2p/discv5cmd.go @@ -84,7 +84,7 @@ func discv5Ping(ctx *cli.Context) error { disc, _ := startV5(ctx) defer disc.Close() - fmt.Println(disc.Ping(n)) + fmt.Println(disc.PingWithoutResp(n)) return nil } diff --git a/p2p/discover/table.go b/p2p/discover/table.go index 392279f905..b3d18ae41f 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -694,3 +694,49 @@ func pushNode(list []*tableNode, n *tableNode, max int) ([]*tableNode, *tableNod list[0] = n return list, removed } + +// waitInit waits until the table is initialized. +func (tab *Table) waitInit() { + <-tab.initDone +} + +// nodeList returns all nodes contained in the table. +func (tab *Table) nodeList() []*enode.Node { + if !tab.isInitDone() { + return nil + } + + tab.mutex.Lock() + defer tab.mutex.Unlock() + + var nodes []*enode.Node + for _, b := range &tab.buckets { + for _, n := range b.entries { + nodes = append(nodes, n.Node) + } + } + return nodes +} + +// nodeIds returns the node IDs hex representation in the table. +func (tab *Table) nodeIds() [][]string { + tab.mutex.Lock() + defer tab.mutex.Unlock() + nodes := make([][]string, 0) + for _, b := range &tab.buckets { + bucketNodes := make([]string, 0) + for _, n := range b.entries { + bucketNodes = append(bucketNodes, fmt.Sprintf("0x%s", n.ID().String())) + } + nodes = append(nodes, bucketNodes) + } + return nodes +} + +// deleteNode removes a node from the table. +func (tab *Table) deleteNode(n *enode.Node) { + tab.mutex.Lock() + defer tab.mutex.Unlock() + b := tab.bucket(n.ID()) + tab.deleteInBucket(b, n.ID()) +} diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index 29ae5f2c08..717fd00a03 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -210,8 +210,8 @@ func (t *UDPv4) ourEndpoint() v4wire.Endpoint { return v4wire.NewEndpoint(addr, uint16(node.TCP())) } -// Ping sends a ping message to the given node. -func (t *UDPv4) Ping(n *enode.Node) error { +// PingWithoutResp sends a ping message to the given node. +func (t *UDPv4) PingWithoutResp(n *enode.Node) error { _, err := t.ping(n) return err } diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index 4453d10695..2a038c4fd2 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -200,8 +200,8 @@ func (t *UDPv5) Close() { }) } -// Ping sends a ping message to the given node. -func (t *UDPv5) Ping(n *enode.Node) error { +// PingWithoutResp sends a ping message to the given node. +func (t *UDPv5) PingWithoutResp(n *enode.Node) error { _, err := t.ping(n) return err } @@ -226,21 +226,62 @@ func (t *UDPv5) Resolve(n *enode.Node) *enode.Node { return n } -// AllNodes returns all the nodes stored in the local table. -func (t *UDPv5) AllNodes() []*enode.Node { - t.tab.mutex.Lock() - defer t.tab.mutex.Unlock() - nodes := make([]*enode.Node, 0) +// ResolveNodeId searches for a specific Node with the given ID. +// It returns nil if the nodeId could not be resolved. +func (t *UDPv5) ResolveNodeId(id enode.ID) *enode.Node { + if id == t.Self().ID() { + return t.Self() + } - for _, b := range &t.tab.buckets { - for _, n := range b.entries { - nodes = append(nodes, n.Node) + n := t.tab.getNode(id) + if n != nil { + // Try asking directly. This works if the Node is still responding on the endpoint we have. + if resp, err := t.RequestENR(n); err == nil { + return resp } } - return nodes + + // Otherwise do a network lookup. + result := t.Lookup(id) + for _, rn := range result { + if rn.ID() == id { + if n != nil && rn.Seq() <= n.Seq() { + return n + } else { + return rn + } + } + } + + return n } -// LocalNode returns the current local node running the +// AllNodes returns all the nodes stored in the local table. +func (t *UDPv5) AllNodes() []*enode.Node { + return t.tab.nodeList() +} + +// RoutingTableInfo returns the routing table information. Used for Portal discv5 RoutingTableInfo API. +func (t *UDPv5) RoutingTableInfo() [][]string { + return t.tab.nodeIds() +} + +// AddKnownNode adds a node to the routing table. Used for Portal discv5 AddEnr API. +func (t *UDPv5) AddKnownNode(n *enode.Node) bool { + return t.tab.addFoundNode(n, true) +} + +// DeleteNode removes a node from the routing table. Used for Portal discv5 DeleteEnr API. +func (t *UDPv5) DeleteNode(n *enode.Node) { + t.tab.deleteNode(n) +} + +// WaitInit waits for the routing table to be initialized. +func (t *UDPv5) WaitInit() { + t.tab.waitInit() +} + +// LocalNode returns the current local Node running the // protocol. func (t *UDPv5) LocalNode() *enode.LocalNode { return t.localNode @@ -328,7 +369,7 @@ func (t *UDPv5) lookupWorker(destNode *enode.Node, target enode.ID) ([]*enode.No err error ) var r []*enode.Node - r, err = t.findnode(destNode, dists) + r, err = t.Findnode(destNode, dists) if errors.Is(err, errClosed) { return nil, err } @@ -359,21 +400,31 @@ func lookupDistances(target, dest enode.ID) (dists []uint) { // ping calls PING on a node and waits for a PONG response. func (t *UDPv5) ping(n *enode.Node) (uint64, error) { + pong, err := t.PingWithResp(n) + if err != nil { + return 0, err + } + + return pong.ENRSeq, nil +} + +// PingWithResp calls PING on a node and waits for a PONG response. +func (t *UDPv5) PingWithResp(n *enode.Node) (*v5wire.Pong, error) { req := &v5wire.Ping{ENRSeq: t.localNode.Node().Seq()} resp := t.callToNode(n, v5wire.PongMsg, req) defer t.callDone(resp) select { case pong := <-resp.ch: - return pong.(*v5wire.Pong).ENRSeq, nil + return pong.(*v5wire.Pong), nil case err := <-resp.err: - return 0, err + return nil, err } } // RequestENR requests n's record. func (t *UDPv5) RequestENR(n *enode.Node) (*enode.Node, error) { - nodes, err := t.findnode(n, []uint{0}) + nodes, err := t.Findnode(n, []uint{0}) if err != nil { return nil, err } @@ -383,8 +434,8 @@ func (t *UDPv5) RequestENR(n *enode.Node) (*enode.Node, error) { return nodes[0], nil } -// findnode calls FINDNODE on a node and waits for responses. -func (t *UDPv5) findnode(n *enode.Node, distances []uint) ([]*enode.Node, error) { +// Findnode calls FINDNODE on a node and waits for responses. +func (t *UDPv5) Findnode(n *enode.Node, distances []uint) ([]*enode.Node, error) { resp := t.callToNode(n, v5wire.NodesMsg, &v5wire.Findnode{Distances: distances}) return t.waitForNodes(resp, distances) } @@ -736,8 +787,8 @@ func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr netip.AddrPort, p v return true } -// getNode looks for a node record in table and database. -func (t *UDPv5) getNode(id enode.ID) *enode.Node { +// GetNode looks for a node record in table and database. +func (t *UDPv5) GetNode(id enode.ID) *enode.Node { if n := t.tab.getNode(id); n != nil { return n } @@ -776,7 +827,7 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr netip.AddrPort func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr netip.AddrPort) { challenge := &v5wire.Whoareyou{Nonce: p.Nonce} crand.Read(challenge.IDNonce[:]) - if n := t.getNode(fromID); n != nil { + if n := t.GetNode(fromID); n != nil { challenge.Node = n challenge.RecordSeq = n.Seq() } diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index 8631b918ff..371f414760 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -288,7 +288,7 @@ func TestUDPv5_findnodeCall(t *testing.T) { ) go func() { var err error - response, err = test.udp.findnode(remote, distances) + response, err = test.udp.Findnode(remote, distances) done <- err }() @@ -398,7 +398,7 @@ func TestUDPv5_callTimeoutReset(t *testing.T) { done = make(chan error, 1) ) go func() { - _, err := test.udp.findnode(remote, []uint{distance}) + _, err := test.udp.Findnode(remote, []uint{distance}) done <- err }()