github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/blockchain/info.go (about)

     1  package blockchain
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/emicklei/dot"
     9  	"github.com/prysmaticlabs/prysm/shared/params"
    10  )
    11  
    12  const template = `<html>
    13  <head>
    14      <script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/2.1.2/viz.js"></script>
    15      <script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/2.1.2/full.render.js"></script>
    16  <body>
    17      <script type="application/javascript">
    18          var graph = ` + "`%s`;" + `
    19          var viz = new Viz();
    20          viz.renderSVGElement(graph) // reading the graph.
    21              .then(function(element) {
    22                  document.body.appendChild(element); // appends to document.
    23              })
    24              .catch(error => {
    25                  // Create a new Viz instance (@see Caveats page for more info)
    26                  viz = new Viz();
    27                  // Possibly display the error
    28                  console.error(error);
    29              });
    30      </script>
    31  </head>
    32  </body>
    33  </html>`
    34  
    35  // TreeHandler is a handler to serve /tree page in metrics.
    36  func (s *Service) TreeHandler(w http.ResponseWriter, r *http.Request) {
    37  	headState, err := s.HeadState(r.Context())
    38  	if err != nil {
    39  		log.WithError(err).Error("Could not get head state")
    40  		return
    41  	}
    42  	if headState == nil || headState.IsNil() {
    43  		if _, err := w.Write([]byte("Unavailable during initial syncing")); err != nil {
    44  			log.WithError(err).Error("Failed to render p2p info page")
    45  		}
    46  	}
    47  
    48  	nodes := s.cfg.ForkChoiceStore.Nodes()
    49  
    50  	graph := dot.NewGraph(dot.Directed)
    51  	graph.Attr("rankdir", "RL")
    52  	graph.Attr("labeljust", "l")
    53  
    54  	dotNodes := make([]*dot.Node, len(nodes))
    55  	avgBalance := uint64(averageBalance(headState.Balances()))
    56  
    57  	for i := len(nodes) - 1; i >= 0; i-- {
    58  		// Construct label for each node.
    59  		slot := fmt.Sprintf("%d", nodes[i].Slot())
    60  		weight := fmt.Sprintf("%d", nodes[i].Weight()/1e9) // Convert unit Gwei to unit ETH.
    61  		votes := fmt.Sprintf("%d", nodes[i].Weight()/1e9/avgBalance)
    62  		index := fmt.Sprintf("%d", i)
    63  		g := nodes[i].Graffiti()
    64  		graffiti := hex.EncodeToString(g[:8])
    65  		label := "slot: " + slot + "\n votes: " + votes + "\n weight: " + weight + "\n graffiti: " + graffiti
    66  		var dotN dot.Node
    67  		if nodes[i].Parent() != ^uint64(0) {
    68  			dotN = graph.Node(index).Box().Attr("label", label)
    69  		}
    70  
    71  		if nodes[i].Slot() == s.HeadSlot() &&
    72  			nodes[i].BestDescendant() == ^uint64(0) &&
    73  			nodes[i].Parent() != ^uint64(0) {
    74  			dotN = dotN.Attr("color", "green")
    75  		}
    76  
    77  		dotNodes[i] = &dotN
    78  	}
    79  
    80  	for i := len(nodes) - 1; i >= 0; i-- {
    81  		if nodes[i].Parent() != ^uint64(0) && nodes[i].Parent() < uint64(len(dotNodes)) {
    82  			graph.Edge(*dotNodes[i], *dotNodes[nodes[i].Parent()])
    83  		}
    84  	}
    85  
    86  	w.WriteHeader(http.StatusOK)
    87  	w.Header().Set("Content-Type", "text/html")
    88  	if _, err := fmt.Fprintf(w, template, graph.String()); err != nil {
    89  		log.WithError(err).Error("Failed to render p2p info page")
    90  	}
    91  }
    92  
    93  func averageBalance(balances []uint64) float64 {
    94  	total := uint64(0)
    95  	for i := 0; i < len(balances); i++ {
    96  		total += balances[i]
    97  	}
    98  	return float64(total) / float64(len(balances)) / float64(params.BeaconConfig().GweiPerEth)
    99  }