You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

152 lines
3.7 KiB

package debugapi
import (
"context"
"net"
"net/http"
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/db/statedb"
"github.com/hermeznetwork/hermez-node/log"
"github.com/hermeznetwork/hermez-node/synchronizer"
"github.com/hermeznetwork/tracerr"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func handleNoRoute(c *gin.Context) {
c.JSON(http.StatusNotFound, gin.H{
"error": "404 page not found",
})
}
type errorMsg struct {
Message string
}
func badReq(err error, c *gin.Context) {
log.Errorw("Bad request", "err", err)
c.JSON(http.StatusBadRequest, errorMsg{
Message: err.Error(),
})
}
// DebugAPI is an http API with debugging endpoints
type DebugAPI struct {
addr string
stateDB *statedb.StateDB // synchronizer statedb
sync *synchronizer.Synchronizer
}
// NewDebugAPI creates a new DebugAPI
func NewDebugAPI(addr string, stateDB *statedb.StateDB, sync *synchronizer.Synchronizer) *DebugAPI {
return &DebugAPI{
addr: addr,
stateDB: stateDB,
sync: sync,
}
}
func (a *DebugAPI) handleAccount(c *gin.Context) {
uri := struct {
Idx uint32
}{}
if err := c.ShouldBindUri(&uri); err != nil {
badReq(err, c)
return
}
account, err := a.stateDB.LastGetAccount(common.Idx(uri.Idx))
if err != nil {
badReq(err, c)
return
}
c.JSON(http.StatusOK, account)
}
func (a *DebugAPI) handleAccounts(c *gin.Context) {
var accounts []common.Account
if err := a.stateDB.LastRead(func(sdb *statedb.Last) error {
var err error
accounts, err = sdb.GetAccounts()
return err
}); err != nil {
badReq(err, c)
return
}
c.JSON(http.StatusOK, accounts)
}
func (a *DebugAPI) handleCurrentBatch(c *gin.Context) {
batchNum, err := a.stateDB.LastGetCurrentBatch()
if err != nil {
badReq(err, c)
return
}
c.JSON(http.StatusOK, batchNum)
}
func (a *DebugAPI) handleMTRoot(c *gin.Context) {
root, err := a.stateDB.LastMTGetRoot()
if err != nil {
badReq(err, c)
return
}
c.JSON(http.StatusOK, root)
}
func (a *DebugAPI) handleSyncStats(c *gin.Context) {
stats := a.sync.Stats()
c.JSON(http.StatusOK, stats)
}
// Run starts the http server of the DebugAPI. To stop it, pass a context with
// cancelation (see `debugapi_test.go` for an example).
func (a *DebugAPI) Run(ctx context.Context) error {
api := gin.Default()
api.NoRoute(handleNoRoute)
api.Use(cors.Default())
debugAPI := api.Group("/debug")
debugAPI.GET("/metrics", gin.WrapH(promhttp.Handler()))
debugAPI.GET("sdb/batchnum", a.handleCurrentBatch)
debugAPI.GET("sdb/mtroot", a.handleMTRoot)
// Accounts returned by these endpoints will always have BatchNum = 0,
// because the stateDB doesn't store the BatchNum in which an account
// is created.
debugAPI.GET("sdb/accounts", a.handleAccounts)
debugAPI.GET("sdb/accounts/:Idx", a.handleAccount)
debugAPI.GET("sync/stats", a.handleSyncStats)
debugAPIServer := &http.Server{
Handler: api,
// Use some hardcoded numbers that are suitable for testing
ReadTimeout: 30 * time.Second, //nolint:gomnd
WriteTimeout: 30 * time.Second, //nolint:gomnd
MaxHeaderBytes: 1 << 20, //nolint:gomnd
}
listener, err := net.Listen("tcp", a.addr)
if err != nil {
return tracerr.Wrap(err)
}
log.Infof("DebugAPI is ready at %v", a.addr)
go func() {
if err := debugAPIServer.Serve(listener); err != nil &&
tracerr.Unwrap(err) != http.ErrServerClosed {
log.Fatalf("Listen: %s\n", err)
}
}()
<-ctx.Done()
log.Info("Stopping DebugAPI...")
ctxTimeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) //nolint:gomnd
defer cancel()
if err := debugAPIServer.Shutdown(ctxTimeout); err != nil {
return tracerr.Wrap(err)
}
log.Info("DebugAPI done")
return nil
}