erigon-pulse/diagnostics/peers.go

151 lines
4.6 KiB
Go

package diagnostics
import (
"encoding/json"
"net/http"
diagnint "github.com/ledgerwatch/erigon-lib/diagnostics"
"github.com/ledgerwatch/erigon/turbo/node"
"github.com/urfave/cli/v2"
)
type PeerNetworkInfo struct {
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"`
}
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
}
func SetupPeersAccess(ctxclient *cli.Context, metricsMux *http.ServeMux, node *node.ErigonNode) {
metricsMux.HandleFunc("/peers", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Content-Type", "application/json")
writePeers(w, ctxclient, node)
})
}
func writePeers(w http.ResponseWriter, ctx *cli.Context, node *node.ErigonNode) {
sentinelPeers, err := sentinelPeers(node)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
sentryPeers, err := sentryPeers(node)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
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{
ENR: "", //TODO: find a way how to get missing data
Enode: "",
ID: key,
Name: "",
Type: "Sentinel",
Caps: []string{},
Network: PeerNetworkInfo{
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,
},
Protocols: nil,
}
peers = append(peers, &peer)
}
return peers, nil
} else {
return []*PeerResponse{}, nil
}
}
func sentryPeers(node *node.ErigonNode) ([]*PeerResponse, error) {
statisticsArray := node.Backend().DiagnosticsPeersData()
peers := make([]*PeerResponse, 0, len(statisticsArray))
for key, value := range statisticsArray {
peer := PeerResponse{
ENR: "", //TODO: find a way how to get missing data
Enode: "",
ID: key,
Name: "",
Type: "Sentry",
Caps: []string{},
Network: PeerNetworkInfo{
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,
},
Protocols: nil,
}
peers = append(peers, &peer)
}
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
}