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