arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/test/debugapi/debugapi.go
test/debugapi/debugapi.goRaw
  1package debugapi
  2
  3import (
  4	"context"
  5	"net"
  6	"net/http"
  7	"time"
  8
  9	"github.com/gin-contrib/cors"
 10	"github.com/gin-gonic/gin"
 11	"github.com/hermeznetwork/hermez-node/common"
 12	"github.com/hermeznetwork/hermez-node/db/statedb"
 13	"github.com/hermeznetwork/hermez-node/log"
 14	"github.com/hermeznetwork/hermez-node/synchronizer"
 15	"github.com/hermeznetwork/tracerr"
 16	"github.com/prometheus/client_golang/prometheus/promhttp"
 17)
 18
 19func handleNoRoute(c *gin.Context) {
 20	c.JSON(http.StatusNotFound, gin.H{
 21		"error": "404 page not found",
 22	})
 23}
 24
 25type errorMsg struct {
 26	Message string
 27}
 28
 29func badReq(err error, c *gin.Context) {
 30	log.Errorw("Bad request", "err", err)
 31	c.JSON(http.StatusBadRequest, errorMsg{
 32		Message: err.Error(),
 33	})
 34}
 35
 36// DebugAPI is an http API with debugging endpoints
 37type DebugAPI struct {
 38	addr    string
 39	stateDB *statedb.StateDB // synchronizer statedb
 40	sync    *synchronizer.Synchronizer
 41}
 42
 43// NewDebugAPI creates a new DebugAPI
 44func NewDebugAPI(addr string, stateDB *statedb.StateDB, sync *synchronizer.Synchronizer) *DebugAPI {
 45	return &DebugAPI{
 46		addr:    addr,
 47		stateDB: stateDB,
 48		sync:    sync,
 49	}
 50}
 51
 52func (a *DebugAPI) handleAccount(c *gin.Context) {
 53	uri := struct {
 54		Idx uint32
 55	}{}
 56	if err := c.ShouldBindUri(&uri); err != nil {
 57		badReq(err, c)
 58		return
 59	}
 60	account, err := a.stateDB.LastGetAccount(common.Idx(uri.Idx))
 61	if err != nil {
 62		badReq(err, c)
 63		return
 64	}
 65	c.JSON(http.StatusOK, account)
 66}
 67
 68func (a *DebugAPI) handleAccounts(c *gin.Context) {
 69	var accounts []common.Account
 70	if err := a.stateDB.LastRead(func(sdb *statedb.Last) error {
 71		var err error
 72		accounts, err = sdb.GetAccounts()
 73		return err
 74	}); err != nil {
 75		badReq(err, c)
 76		return
 77	}
 78	c.JSON(http.StatusOK, accounts)
 79}
 80
 81func (a *DebugAPI) handleCurrentBatch(c *gin.Context) {
 82	batchNum, err := a.stateDB.LastGetCurrentBatch()
 83	if err != nil {
 84		badReq(err, c)
 85		return
 86	}
 87	c.JSON(http.StatusOK, batchNum)
 88}
 89
 90func (a *DebugAPI) handleMTRoot(c *gin.Context) {
 91	root, err := a.stateDB.LastMTGetRoot()
 92	if err != nil {
 93		badReq(err, c)
 94		return
 95	}
 96	c.JSON(http.StatusOK, root)
 97}
 98
 99func (a *DebugAPI) handleSyncStats(c *gin.Context) {
100	stats := a.sync.Stats()
101	c.JSON(http.StatusOK, stats)
102}
103
104// Run starts the http server of the DebugAPI.  To stop it, pass a context with
105// cancelation (see `debugapi_test.go` for an example).
106func (a *DebugAPI) Run(ctx context.Context) error {
107	api := gin.Default()
108	api.NoRoute(handleNoRoute)
109	api.Use(cors.Default())
110	debugAPI := api.Group("/debug")
111
112	debugAPI.GET("/metrics", gin.WrapH(promhttp.Handler()))
113
114	debugAPI.GET("sdb/batchnum", a.handleCurrentBatch)
115	debugAPI.GET("sdb/mtroot", a.handleMTRoot)
116	// Accounts returned by these endpoints will always have BatchNum = 0,
117	// because the stateDB doesn't store the BatchNum in which an account
118	// is created.
119	debugAPI.GET("sdb/accounts", a.handleAccounts)
120	debugAPI.GET("sdb/accounts/:Idx", a.handleAccount)
121
122	debugAPI.GET("sync/stats", a.handleSyncStats)
123
124	debugAPIServer := &http.Server{
125		Handler: api,
126		// Use some hardcoded numbers that are suitable for testing
127		ReadTimeout:    30 * time.Second, //nolint:gomnd
128		WriteTimeout:   30 * time.Second, //nolint:gomnd
129		MaxHeaderBytes: 1 << 20,          //nolint:gomnd
130	}
131	listener, err := net.Listen("tcp", a.addr)
132	if err != nil {
133		return tracerr.Wrap(err)
134	}
135	log.Infof("DebugAPI is ready at %v", a.addr)
136	go func() {
137		if err := debugAPIServer.Serve(listener); err != nil &&
138			tracerr.Unwrap(err) != http.ErrServerClosed {
139			log.Fatalf("Listen: %s\n", err)
140		}
141	}()
142
143	<-ctx.Done()
144	log.Info("Stopping DebugAPI...")
145	ctxTimeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) //nolint:gomnd
146	defer cancel()
147	if err := debugAPIServer.Shutdown(ctxTimeout); err != nil {
148		return tracerr.Wrap(err)
149	}
150	log.Info("DebugAPI done")
151	return nil
152}