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