2019-09-06 03:04:25 +00:00
|
|
|
package blockchain
|
|
|
|
|
|
|
|
import (
|
2020-05-16 19:29:21 +00:00
|
|
|
"encoding/hex"
|
2019-09-06 03:04:25 +00:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
2020-01-26 20:25:33 +00:00
|
|
|
"github.com/emicklei/dot"
|
2020-07-16 22:44:43 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2019-09-06 03:04:25 +00:00
|
|
|
)
|
|
|
|
|
2020-02-05 01:14:43 +00:00
|
|
|
const template = `<html>
|
|
|
|
<head>
|
|
|
|
<script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/2.1.2/viz.js"></script>
|
|
|
|
<script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/2.1.2/full.render.js"></script>
|
|
|
|
<body>
|
|
|
|
<script type="application/javascript">
|
|
|
|
var graph = ` + "`%s`;" + `
|
|
|
|
var viz = new Viz();
|
|
|
|
viz.renderSVGElement(graph) // reading the graph.
|
|
|
|
.then(function(element) {
|
|
|
|
document.body.appendChild(element); // appends to document.
|
|
|
|
})
|
|
|
|
.catch(error => {
|
|
|
|
// Create a new Viz instance (@see Caveats page for more info)
|
|
|
|
viz = new Viz();
|
|
|
|
// Possibly display the error
|
|
|
|
console.error(error);
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
</head>
|
|
|
|
</body>
|
|
|
|
</html>`
|
|
|
|
|
2020-01-26 20:25:33 +00:00
|
|
|
// TreeHandler is a handler to serve /tree page in metrics.
|
2020-06-26 22:52:11 +00:00
|
|
|
func (s *Service) TreeHandler(w http.ResponseWriter, r *http.Request) {
|
2020-10-06 10:57:18 +00:00
|
|
|
headState, err := s.HeadState(r.Context())
|
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).Error("Could not get head state")
|
|
|
|
return
|
|
|
|
}
|
2021-05-24 04:55:42 +00:00
|
|
|
if headState == nil || headState.IsNil() {
|
2020-02-06 04:49:12 +00:00
|
|
|
if _, err := w.Write([]byte("Unavailable during initial syncing")); err != nil {
|
|
|
|
log.WithError(err).Error("Failed to render p2p info page")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 18:36:56 +00:00
|
|
|
nodes := s.cfg.ForkChoiceStore.Nodes()
|
2020-01-26 20:25:33 +00:00
|
|
|
|
|
|
|
graph := dot.NewGraph(dot.Directed)
|
|
|
|
graph.Attr("rankdir", "RL")
|
|
|
|
graph.Attr("labeljust", "l")
|
|
|
|
|
|
|
|
dotNodes := make([]*dot.Node, len(nodes))
|
2020-10-06 10:57:18 +00:00
|
|
|
avgBalance := uint64(averageBalance(headState.Balances()))
|
2020-02-05 23:30:55 +00:00
|
|
|
|
2020-01-26 20:25:33 +00:00
|
|
|
for i := len(nodes) - 1; i >= 0; i-- {
|
|
|
|
// Construct label for each node.
|
2020-08-08 01:37:08 +00:00
|
|
|
slot := fmt.Sprintf("%d", nodes[i].Slot())
|
|
|
|
weight := fmt.Sprintf("%d", nodes[i].Weight()/1e9) // Convert unit Gwei to unit ETH.
|
|
|
|
votes := fmt.Sprintf("%d", nodes[i].Weight()/1e9/avgBalance)
|
2020-06-26 16:07:00 +00:00
|
|
|
index := fmt.Sprintf("%d", i)
|
2020-08-08 01:37:08 +00:00
|
|
|
g := nodes[i].Graffiti()
|
2020-05-16 19:29:21 +00:00
|
|
|
graffiti := hex.EncodeToString(g[:8])
|
|
|
|
label := "slot: " + slot + "\n votes: " + votes + "\n weight: " + weight + "\n graffiti: " + graffiti
|
2020-01-26 20:25:33 +00:00
|
|
|
var dotN dot.Node
|
2020-08-08 01:37:08 +00:00
|
|
|
if nodes[i].Parent() != ^uint64(0) {
|
2020-01-26 20:25:33 +00:00
|
|
|
dotN = graph.Node(index).Box().Attr("label", label)
|
|
|
|
}
|
2020-02-05 23:30:55 +00:00
|
|
|
|
2020-10-06 10:57:18 +00:00
|
|
|
if nodes[i].Slot() == s.HeadSlot() &&
|
2020-08-08 01:37:08 +00:00
|
|
|
nodes[i].BestDescendant() == ^uint64(0) &&
|
|
|
|
nodes[i].Parent() != ^uint64(0) {
|
2020-02-05 23:30:55 +00:00
|
|
|
dotN = dotN.Attr("color", "green")
|
|
|
|
}
|
|
|
|
|
2020-01-26 20:25:33 +00:00
|
|
|
dotNodes[i] = &dotN
|
|
|
|
}
|
|
|
|
|
2020-01-27 21:48:16 +00:00
|
|
|
for i := len(nodes) - 1; i >= 0; i-- {
|
2020-08-08 01:37:08 +00:00
|
|
|
if nodes[i].Parent() != ^uint64(0) && nodes[i].Parent() < uint64(len(dotNodes)) {
|
|
|
|
graph.Edge(*dotNodes[i], *dotNodes[nodes[i].Parent()])
|
2020-01-26 20:25:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
2020-02-05 01:14:43 +00:00
|
|
|
w.Header().Set("Content-Type", "text/html")
|
|
|
|
if _, err := fmt.Fprintf(w, template, graph.String()); err != nil {
|
2020-01-26 20:25:33 +00:00
|
|
|
log.WithError(err).Error("Failed to render p2p info page")
|
|
|
|
}
|
|
|
|
}
|
2020-07-16 22:44:43 +00:00
|
|
|
|
|
|
|
func averageBalance(balances []uint64) float64 {
|
|
|
|
total := uint64(0)
|
|
|
|
for i := 0; i < len(balances); i++ {
|
|
|
|
total += balances[i]
|
|
|
|
}
|
|
|
|
return float64(total) / float64(len(balances)) / float64(params.BeaconConfig().GweiPerEth)
|
|
|
|
}
|