2023-09-28 13:02:02 +00:00
|
|
|
package diagnostics
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"net/http"
|
|
|
|
|
2023-10-23 14:33:08 +00:00
|
|
|
diagnint "github.com/ledgerwatch/erigon-lib/diagnostics"
|
2023-09-28 13:02:02 +00:00
|
|
|
"github.com/ledgerwatch/erigon/turbo/node"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
)
|
|
|
|
|
2023-10-23 14:33:08 +00:00
|
|
|
type PeerNetworkInfo struct {
|
2023-10-27 19:30:28 +00:00
|
|
|
LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection
|
|
|
|
RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection
|
|
|
|
Inbound bool `json:"inbound"`
|
|
|
|
Trusted bool `json:"trusted"`
|
|
|
|
Static bool `json:"static"`
|
|
|
|
BytesIn uint64 `json:"bytesIn"`
|
|
|
|
BytesOut uint64 `json:"bytesOut"`
|
|
|
|
CapBytesIn map[string]uint64 `json:"capBytesIn"`
|
|
|
|
CapBytesOut map[string]uint64 `json:"capBytesOut"`
|
|
|
|
TypeBytesIn map[string]uint64 `json:"typeBytesIn"`
|
|
|
|
TypeBytesOut map[string]uint64 `json:"typeBytesOut"`
|
2023-10-23 14:33:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type PeerResponse struct {
|
|
|
|
ENR string `json:"enr,omitempty"` // Ethereum Node Record
|
|
|
|
Enode string `json:"enode"` // Node URL
|
|
|
|
ID string `json:"id"` // Unique node identifier
|
|
|
|
Name string `json:"name"` // Name of the node, including client type, version, OS, custom data
|
|
|
|
ErrorCount int `json:"errorCount"` // Number of errors
|
|
|
|
LastSeenError string `json:"lastSeenError"` // Last seen error
|
|
|
|
Type string `json:"type"` // Type of connection
|
|
|
|
Caps []string `json:"caps"` // Protocols advertised by this peer
|
|
|
|
Network PeerNetworkInfo `json:"network"`
|
|
|
|
Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields
|
|
|
|
}
|
|
|
|
|
2023-11-07 12:50:36 +00:00
|
|
|
func SetupPeersAccess(ctxclient *cli.Context, metricsMux *http.ServeMux, node *node.ErigonNode) {
|
2023-09-28 13:02:02 +00:00
|
|
|
metricsMux.HandleFunc("/peers", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2023-11-07 12:50:36 +00:00
|
|
|
writePeers(w, ctxclient, node)
|
2023-09-28 13:02:02 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func writePeers(w http.ResponseWriter, ctx *cli.Context, node *node.ErigonNode) {
|
2023-10-23 14:33:08 +00:00
|
|
|
sentinelPeers, err := sentinelPeers(node)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2023-09-28 13:02:02 +00:00
|
|
|
|
2023-10-23 14:33:08 +00:00
|
|
|
sentryPeers, err := sentryPeers(node)
|
2023-09-28 13:02:02 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-10-23 14:33:08 +00:00
|
|
|
allPeers := append(sentryPeers, sentinelPeers...)
|
|
|
|
|
|
|
|
json.NewEncoder(w).Encode(allPeers)
|
|
|
|
}
|
|
|
|
|
|
|
|
func sentinelPeers(node *node.ErigonNode) ([]*PeerResponse, error) {
|
|
|
|
if diag, ok := node.Backend().Sentinel().(diagnint.PeerStatisticsGetter); ok {
|
|
|
|
|
|
|
|
statisticsArray := diag.GetPeersStatistics()
|
|
|
|
peers := make([]*PeerResponse, 0, len(statisticsArray))
|
|
|
|
|
|
|
|
for key, value := range statisticsArray {
|
|
|
|
peer := PeerResponse{
|
2023-10-27 19:30:28 +00:00
|
|
|
ENR: "", //TODO: find a way how to get missing data
|
|
|
|
Enode: "",
|
|
|
|
ID: key,
|
|
|
|
Name: "",
|
|
|
|
Type: "Sentinel",
|
|
|
|
Caps: []string{},
|
2023-10-23 14:33:08 +00:00
|
|
|
Network: PeerNetworkInfo{
|
|
|
|
LocalAddress: "",
|
|
|
|
RemoteAddress: "",
|
|
|
|
Inbound: false,
|
|
|
|
Trusted: false,
|
|
|
|
Static: false,
|
2023-10-27 19:30:28 +00:00
|
|
|
BytesIn: value.BytesIn,
|
|
|
|
BytesOut: value.BytesOut,
|
|
|
|
CapBytesIn: value.CapBytesIn,
|
|
|
|
CapBytesOut: value.CapBytesOut,
|
|
|
|
TypeBytesIn: value.TypeBytesIn,
|
|
|
|
TypeBytesOut: value.TypeBytesOut,
|
2023-10-23 14:33:08 +00:00
|
|
|
},
|
|
|
|
Protocols: nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
peers = append(peers, &peer)
|
|
|
|
}
|
|
|
|
|
|
|
|
return peers, nil
|
|
|
|
} else {
|
|
|
|
return []*PeerResponse{}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func sentryPeers(node *node.ErigonNode) ([]*PeerResponse, error) {
|
|
|
|
|
2023-10-27 19:30:28 +00:00
|
|
|
statisticsArray := node.Backend().DiagnosticsPeersData()
|
2023-10-23 14:33:08 +00:00
|
|
|
|
2023-10-27 19:30:28 +00:00
|
|
|
peers := make([]*PeerResponse, 0, len(statisticsArray))
|
2023-10-23 14:33:08 +00:00
|
|
|
|
2023-10-27 19:30:28 +00:00
|
|
|
for key, value := range statisticsArray {
|
2023-10-23 14:33:08 +00:00
|
|
|
peer := PeerResponse{
|
2023-10-27 19:30:28 +00:00
|
|
|
ENR: "", //TODO: find a way how to get missing data
|
|
|
|
Enode: "",
|
|
|
|
ID: key,
|
|
|
|
Name: "",
|
|
|
|
Type: "Sentry",
|
|
|
|
Caps: []string{},
|
2023-10-23 14:33:08 +00:00
|
|
|
Network: PeerNetworkInfo{
|
2023-10-27 19:30:28 +00:00
|
|
|
LocalAddress: "",
|
|
|
|
RemoteAddress: "",
|
|
|
|
Inbound: false,
|
|
|
|
Trusted: false,
|
|
|
|
Static: false,
|
|
|
|
BytesIn: value.BytesIn,
|
|
|
|
BytesOut: value.BytesOut,
|
|
|
|
CapBytesIn: value.CapBytesIn,
|
|
|
|
CapBytesOut: value.CapBytesOut,
|
|
|
|
TypeBytesIn: value.TypeBytesIn,
|
|
|
|
TypeBytesOut: value.TypeBytesOut,
|
2023-09-28 13:02:02 +00:00
|
|
|
},
|
|
|
|
Protocols: nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
peers = append(peers, &peer)
|
|
|
|
}
|
|
|
|
|
2023-10-27 19:30:28 +00:00
|
|
|
return filterPeersWithoutBytesIn(peers), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func filterPeersWithoutBytesIn(peers []*PeerResponse) []*PeerResponse {
|
|
|
|
filteredPeers := make([]*PeerResponse, 0, len(peers))
|
|
|
|
|
|
|
|
for _, peer := range peers {
|
|
|
|
if peer.Network.BytesIn > 0 {
|
|
|
|
filteredPeers = append(filteredPeers, peer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filteredPeers
|
2023-09-28 13:02:02 +00:00
|
|
|
}
|