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
105// with cancellation (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}