Repository: ethereum/go-ethereum (language: go)

Issue:
Expose discv5 function for portal discv5 json rpc
In order to help shisui to impl portal network [discv5 json rpc](https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/ethereum/portal-network-specs/assembled-spec/jsonrpc/openrpc.json&uiSchema%5BappBar%5D%5Bui:splitView%5D=false&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false) endpoints, expose several table functions to public.




Discussion / hints:

Fix patch:
diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go
index 8c48b3a557c1..84c7ef0c44bf 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.Ping(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 2422ef6644c9..dd253dd08239 100644
--- a/cmd/devp2p/discv5cmd.go
+++ b/cmd/devp2p/discv5cmd.go
@@ -84,7 +84,8 @@ func discv5Ping(ctx *cli.Context) error {
 	disc, _ := startV5(ctx)
 	defer disc.Close()
 
-	fmt.Println(disc.Ping(n))
+	_, err := disc.Ping(n)
+	fmt.Println(err)
 	return nil
 }
 
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index 392279f905e4..6a3d1af3af61 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -694,3 +694,11 @@ func pushNode(list []*tableNode, n *tableNode, max int) ([]*tableNode, *tableNod
 	list[0] = n
 	return list, removed
 }
+
+// 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 29ae5f2c084d..dd3f363f7ec0 100644
--- a/p2p/discover/v4_udp.go
+++ b/p2p/discover/v4_udp.go
@@ -210,12 +210,6 @@ 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 {
-	_, err := t.ping(n)
-	return err
-}
-
 // ping sends a ping message to the given node and waits for a reply.
 func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) {
 	addr, ok := n.UDPEndpoint()
@@ -229,6 +223,19 @@ func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) {
 	return seq, err
 }
 
+// Ping calls PING on a node and waits for a PONG response.
+func (t *UDPv4) Ping(n *enode.Node) (pong *v4wire.Pong, err error) {
+	addr, ok := n.UDPEndpoint()
+	if !ok {
+		return nil, errNoUDPEndpoint
+	}
+	rm := t.sendPing(n.ID(), addr, nil)
+	if err = <-rm.errc; err == nil {
+		pong = rm.reply.(*v4wire.Pong)
+	}
+	return pong, err
+}
+
 // sendPing sends a ping message to the given node and invokes the callback
 // when the reply arrives.
 func (t *UDPv4) sendPing(toid enode.ID, toaddr netip.AddrPort, callback func()) *replyMatcher {
diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go
index 4453d1069534..f4da97d0fefc 100644
--- a/p2p/discover/v5_udp.go
+++ b/p2p/discover/v5_udp.go
@@ -200,12 +200,6 @@ func (t *UDPv5) Close() {
 	})
 }
 
-// Ping sends a ping message to the given node.
-func (t *UDPv5) Ping(n *enode.Node) error {
-	_, err := t.ping(n)
-	return err
-}
-
 // Resolve searches for a specific node with the given ID and tries to get the most recent
 // version of the node record for it. It returns n if the node could not be resolved.
 func (t *UDPv5) Resolve(n *enode.Node) *enode.Node {
@@ -226,6 +220,36 @@ func (t *UDPv5) Resolve(n *enode.Node) *enode.Node {
 	return n
 }
 
+// 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()
+	}
+
+	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
+		}
+	}
+
+	// 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
+}
+
 // AllNodes returns all the nodes stored in the local table.
 func (t *UDPv5) AllNodes() []*enode.Node {
 	t.tab.mutex.Lock()
@@ -240,7 +264,18 @@ func (t *UDPv5) AllNodes() []*enode.Node {
 	return nodes
 }
 
-// LocalNode returns the current local node running the
+// AddKnownNode adds a node to the routing table.
+// The function should be used for testing only.
+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)
+}
+
+// LocalNode returns the current local Node running the
 // protocol.
 func (t *UDPv5) LocalNode() *enode.LocalNode {
 	return t.localNode
@@ -328,7 +363,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 +394,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.Ping(n)
+	if err != nil {
+		return 0, err
+	}
+
+	return pong.ENRSeq, nil
+}
+
+// Ping calls PING on a node and waits for a PONG response.
+func (t *UDPv5) Ping(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 +428,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 +781,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
 	}
@@ -747,6 +792,11 @@ func (t *UDPv5) getNode(id enode.ID) *enode.Node {
 	return nil
 }
 
+// Nodes returns the nodes in the routing table.
+func (t *UDPv5) Nodes() [][]BucketNode {
+	return t.tab.Nodes()
+}
+
 // handle processes incoming packets according to their message type.
 func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr netip.AddrPort) {
 	switch p := p.(type) {
@@ -776,7 +826,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()
 	}


Test patch:
diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go
index 8631b918ff08..371f4147601c 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
 	}()