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.LastGetAccount(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 var accounts []common.Account
68 if err := a.stateDB.LastRead(func(sdb *statedb.Last) error {
69 var err error
70 accounts, err = sdb.GetAccounts()
71 return err
72 }); err != nil {
73 badReq(err, c)
74 return
75 }
76 c.JSON(http.StatusOK, accounts)
77}
78
79func (a *DebugAPI) handleCurrentBatch(c *gin.Context) {
80 batchNum, err := a.stateDB.LastGetCurrentBatch()
81 if err != nil {
82 badReq(err, c)
83 return
84 }
85 c.JSON(http.StatusOK, batchNum)
86}
87
88func (a *DebugAPI) handleMTRoot(c *gin.Context) {
89 root, err := a.stateDB.LastMTGetRoot()
90 if err != nil {
91 badReq(err, c)
92 return
93 }
94 c.JSON(http.StatusOK, root)
95}
96
97func (a *DebugAPI) handleSyncStats(c *gin.Context) {
98 stats := a.sync.Stats()
99 c.JSON(http.StatusOK, stats)
100}
101
102// Run starts the http server of the DebugAPI. To stop it, pass a context with
103// cancelation (see `debugapi_test.go` for an example).
104func (a *DebugAPI) Run(ctx context.Context) error {
105 api := gin.Default()
106 api.NoRoute(handleNoRoute)
107 api.Use(cors.Default())
108 debugAPI := api.Group("/debug")
109
110 debugAPI.GET("sdb/batchnum", a.handleCurrentBatch)
111 debugAPI.GET("sdb/mtroot", a.handleMTRoot)
112 // Accounts returned by these endpoints will always have BatchNum = 0,
113 // because the stateDB doesn't store the BatchNum in which an account
114 // is created.
115 debugAPI.GET("sdb/accounts", a.handleAccounts)
116 debugAPI.GET("sdb/accounts/:Idx", a.handleAccount)
117
118 debugAPI.GET("sync/stats", a.handleSyncStats)
119
120 debugAPIServer := &http.Server{
121 Addr: a.addr,
122 Handler: api,
123 // Use some hardcoded numberes that are suitable for testing
124 ReadTimeout: 30 * time.Second, //nolint:gomnd
125 WriteTimeout: 30 * time.Second, //nolint:gomnd
126 MaxHeaderBytes: 1 << 20, //nolint:gomnd
127 }
128 go func() {
129 log.Infof("DebugAPI is ready at %v", a.addr)
130 if err := debugAPIServer.ListenAndServe(); err != nil && tracerr.Unwrap(err) != http.ErrServerClosed {
131 log.Fatalf("Listen: %s\n", err)
132 }
133 }()
134
135 <-ctx.Done()
136 log.Info("Stopping DebugAPI...")
137 ctxTimeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) //nolint:gomnd
138 defer cancel()
139 if err := debugAPIServer.Shutdown(ctxTimeout); err != nil {
140 return tracerr.Wrap(err)
141 }
142 log.Info("DebugAPI done")
143 return nil
144}