hermez-node/test/proofserver/proofserver.go
test/proofserver/proofserver.goRaw
1package proofserver
2
3import (
4 "context"
5 "fmt"
6 "io/ioutil"
7 "net"
8 "net/http"
9 "sync"
10 "time"
11
12 "github.com/gin-contrib/cors"
13 "github.com/gin-gonic/gin"
14 "github.com/hermeznetwork/hermez-node/log"
15 "github.com/hermeznetwork/hermez-node/prover"
16 "github.com/hermeznetwork/tracerr"
17)
18
19type msg struct {
20 value string
21 ackCh chan bool
22}
23
24func newMsg(value string) msg {
25 return msg{
26 value: value,
27 ackCh: make(chan bool),
28 }
29}
30
31// Mock proof server
32type Mock struct {
33 addr string
34 status prover.StatusCode
35 sync.RWMutex
36 proof string
37 pubData string
38 counter int
39 msgCh chan msg
40 wg sync.WaitGroup
41 provingDuration time.Duration
42}
43
44// NewMock creates a new mock server
45func NewMock(addr string, provingDuration time.Duration) *Mock {
46 return &Mock{
47 addr: addr,
48 status: prover.StatusCodeReady,
49 proof: "",
50 pubData: "",
51 counter: 0,
52 msgCh: make(chan msg),
53 provingDuration: provingDuration,
54 }
55}
56
57func (s *Mock) err(c *gin.Context, err error) {
58 c.JSON(http.StatusInternalServerError, prover.ErrorServer{
59 Status: "error",
60 Message: err.Error(),
61 })
62}
63
64func (s *Mock) handleCancel(c *gin.Context) {
65 msg := newMsg("cancel")
66 s.msgCh <- msg
67 <-msg.ackCh
68 c.JSON(http.StatusOK, "OK")
69}
70
71//nolint:lll
72/* Status example from the real server proof:
73
74Status:
75{
76 "proof": "{\n \"pi_a\": [\n \"1368015179489954701390400359078579693043519447331113978918064868415326638035\",\n \"9918110051302171585080402603319702774565515993150576347155970296011118125764\",\n \"1\"\n ],\n \"pi_b\": [\n [\n \"10857046999023057135944570762232829481370756359578518086990519993285655852781\",\n \"11559732032986387107991004021392285783925812861821192530917403151452391805634\"\n ],\n [\n \"8495653923123431417604973247489272438418190587263600148770280649306958101930\",\n \"4082367875863433681332203403145435568316851327593401208105741076214120093531\"\n ],\n [\n \"1\",\n \"0\"\n ]\n ],\n \"pi_c\": [\n \"1368015179489954701390400359078579693043519447331113978918064868415326638035\",\n \"9918110051302171585080402603319702774565515993150576347155970296011118125764\",\n \"1\"\n ],\n \"protocol\": \"groth\"\n}\n",
77 "pubData": "[\n \"8863150934551775031093873719629424744398133643983814385850330952980893030086\"\n]\n",
78 "status": "success"
79}
80
81proof:
82{
83 "pi_a": [
84 "1368015179489954701390400359078579693043519447331113978918064868415326638035",
85 "9918110051302171585080402603319702774565515993150576347155970296011118125764",
86 "1"
87 ],
88 "pi_b": [
89 [
90 "10857046999023057135944570762232829481370756359578518086990519993285655852781",
91 "11559732032986387107991004021392285783925812861821192530917403151452391805634"
92 ],
93 [
94 "8495653923123431417604973247489272438418190587263600148770280649306958101930",
95 "4082367875863433681332203403145435568316851327593401208105741076214120093531"
96 ],
97 [
98 "1",
99 "0"
100 ]
101 ],
102 "pi_c": [
103 "1368015179489954701390400359078579693043519447331113978918064868415326638035",
104 "9918110051302171585080402603319702774565515993150576347155970296011118125764",
105 "1"
106 ],
107 "protocol": "groth"
108}
109
110pubData:
111[
112 "8863150934551775031093873719629424744398133643983814385850330952980893030086"
113]
114*/
115
116func (s *Mock) handleStatus(c *gin.Context) {
117 s.RLock()
118 c.JSON(http.StatusOK, prover.Status{
119 Status: s.status,
120 Proof: s.proof,
121 PubData: s.pubData,
122 })
123 s.RUnlock()
124}
125
126func (s *Mock) handleInput(c *gin.Context) {
127 s.RLock()
128 if !s.status.IsReady() {
129 s.err(c, fmt.Errorf("not ready"))
130 s.RUnlock()
131 return
132 }
133 s.RUnlock()
134 _, err := ioutil.ReadAll(c.Request.Body)
135 if err != nil {
136 s.err(c, err)
137 return
138 }
139 msg := newMsg("prove")
140 s.msgCh <- msg
141 <-msg.ackCh
142 c.JSON(http.StatusOK, "OK")
143}
144
145const longWaitDuration = 999 * time.Hour
146
147// const provingDuration = 2 * time.Second
148
149func (s *Mock) runProver(ctx context.Context) {
150 timer := time.NewTimer(longWaitDuration)
151 for {
152 select {
153 case <-ctx.Done():
154 return
155 case msg := <-s.msgCh:
156 switch msg.value {
157 case "cancel":
158 if !timer.Stop() {
159 <-timer.C
160 }
161 timer.Reset(longWaitDuration)
162 s.Lock()
163 if !s.status.IsReady() {
164 s.status = prover.StatusCodeAborted
165 }
166 s.Unlock()
167 case "prove":
168 if !timer.Stop() {
169 <-timer.C
170 }
171 timer.Reset(s.provingDuration)
172 s.Lock()
173 s.status = prover.StatusCodeBusy
174 s.Unlock()
175 }
176 msg.ackCh <- true
177 case <-timer.C:
178 timer.Reset(longWaitDuration)
179 s.Lock()
180 if s.status != prover.StatusCodeBusy {
181 s.Unlock()
182 continue
183 }
184 i := s.counter * 100 //nolint:gomnd
185 s.counter++
186 // Mock data
187 s.proof = fmt.Sprintf(`{
188 "pi_a": ["%v", "%v", "1"],
189 "pi_b": [["%v", "%v"],["%v", "%v"],["1", "0"]],
190 "pi_c": ["%v", "%v", "1"],
191 "protocol": "groth"
192 }`, i, i+1, i+2, i+3, i+4, i+5, i+6, i+7) //nolint:gomnd
193 s.pubData = fmt.Sprintf(`[
194 "%v"
195 ]`, i+42) //nolint:gomnd
196 s.status = prover.StatusCodeSuccess
197 s.Unlock()
198 }
199 }
200}
201
202// Run the mock server. Use ctx to stop it via cancel
203func (s *Mock) Run(ctx context.Context) error {
204 api := gin.Default()
205 api.Use(cors.Default())
206
207 apiGroup := api.Group("/api")
208 apiGroup.GET("/status", s.handleStatus)
209 apiGroup.POST("/input", s.handleInput)
210 apiGroup.POST("/cancel", s.handleCancel)
211
212 debugAPIServer := &http.Server{
213 Handler: api,
214 // Use some hardcoded numberes that are suitable for testing
215 ReadTimeout: 30 * time.Second, //nolint:gomnd
216 WriteTimeout: 30 * time.Second, //nolint:gomnd
217 MaxHeaderBytes: 1 << 20, //nolint:gomnd
218 }
219 listener, err := net.Listen("tcp", s.addr)
220 if err != nil {
221 return tracerr.Wrap(err)
222 }
223 log.Infof("prover.MockServer is ready at %v", s.addr)
224 go func() {
225 if err := debugAPIServer.Serve(listener); err != nil &&
226 tracerr.Unwrap(err) != http.ErrServerClosed {
227 log.Fatalf("Listen: %s\n", err)
228 }
229 }()
230 s.wg.Add(1)
231 go func() {
232 s.runProver(ctx)
233 s.wg.Done()
234 }()
235
236 <-ctx.Done()
237 log.Info("Stopping prover.MockServer...")
238
239 s.wg.Wait()
240 ctxTimeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) //nolint:gomnd
241 defer cancel()
242 if err := debugAPIServer.Shutdown(ctxTimeout); err != nil {
243 return tracerr.Wrap(err)
244 }
245 log.Info("prover.MockServer done")
246 return nil
247}