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