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