arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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/* Status example from the real server proof:
 72
 73Status:
 74{
 75  "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",
 76  "pubData": "[\n    \"8863150934551775031093873719629424744398133643983814385850330952980893030086\"\n]\n",
 77  "status": "success"
 78}
 79
 80proof:
 81{
 82    "pi_a": [
 83        "1368015179489954701390400359078579693043519447331113978918064868415326638035",
 84        "9918110051302171585080402603319702774565515993150576347155970296011118125764",
 85        "1"
 86    ],
 87    "pi_b": [
 88        [
 89            "10857046999023057135944570762232829481370756359578518086990519993285655852781",
 90            "11559732032986387107991004021392285783925812861821192530917403151452391805634"
 91        ],
 92        [
 93            "8495653923123431417604973247489272438418190587263600148770280649306958101930",
 94            "4082367875863433681332203403145435568316851327593401208105741076214120093531"
 95        ],
 96        [
 97            "1",
 98            "0"
 99        ]
100    ],
101    "pi_c": [
102        "1368015179489954701390400359078579693043519447331113978918064868415326638035",
103        "9918110051302171585080402603319702774565515993150576347155970296011118125764",
104        "1"
105    ],
106    "protocol": "groth"
107}
108
109pubData:
110[
111    "8863150934551775031093873719629424744398133643983814385850330952980893030086"
112]
113*/
114
115func (s *Mock) handleStatus(c *gin.Context) {
116	s.RLock()
117	c.JSON(http.StatusOK, prover.Status{
118		Status:  s.status,
119		Proof:   s.proof,
120		PubData: s.pubData,
121	})
122	s.RUnlock()
123}
124
125func (s *Mock) handleInput(c *gin.Context) {
126	s.RLock()
127	if !s.status.IsReady() {
128		s.err(c, fmt.Errorf("not ready"))
129		s.RUnlock()
130		return
131	}
132	s.RUnlock()
133	_, err := ioutil.ReadAll(c.Request.Body)
134	if err != nil {
135		s.err(c, err)
136		return
137	}
138	msg := newMsg("prove")
139	s.msgCh <- msg
140	<-msg.ackCh
141	c.JSON(http.StatusOK, "OK")
142}
143
144const longWaitDuration = 999 * time.Hour
145
146// const provingDuration = 2 * time.Second
147
148func (s *Mock) runProver(ctx context.Context) {
149	timer := time.NewTimer(longWaitDuration)
150	for {
151		select {
152		case <-ctx.Done():
153			return
154		case msg := <-s.msgCh:
155			switch msg.value {
156			case "cancel":
157				if !timer.Stop() {
158					<-timer.C
159				}
160				timer.Reset(longWaitDuration)
161				s.Lock()
162				if !s.status.IsReady() {
163					s.status = prover.StatusCodeAborted
164				}
165				s.Unlock()
166			case "prove":
167				if !timer.Stop() {
168					<-timer.C
169				}
170				timer.Reset(s.provingDuration)
171				s.Lock()
172				s.status = prover.StatusCodeBusy
173				s.Unlock()
174			}
175			msg.ackCh <- true
176		case <-timer.C:
177			timer.Reset(longWaitDuration)
178			s.Lock()
179			if s.status != prover.StatusCodeBusy {
180				s.Unlock()
181				continue
182			}
183			i := s.counter * 100 //nolint:gomnd
184			s.counter++
185			// Mock data
186			s.proof = fmt.Sprintf(`{
187				"pi_a": ["%v", "%v", "1"],
188				"pi_b": [["%v", "%v"],["%v", "%v"],["1", "0"]],
189				"pi_c": ["%v", "%v", "1"],
190				"protocol": "groth"
191			}`, i, i+1, i+2, i+3, i+4, i+5, i+6, i+7) //nolint:gomnd
192			s.pubData = fmt.Sprintf(`[
193				"%v"
194			]`, i+42) //nolint:gomnd
195			s.status = prover.StatusCodeSuccess
196			s.Unlock()
197		}
198	}
199}
200
201// Run the mock server.  Use ctx to stop it via cancel
202func (s *Mock) Run(ctx context.Context) error {
203	api := gin.Default()
204	api.Use(cors.Default())
205
206	apiGroup := api.Group("/api")
207	apiGroup.GET("/status", s.handleStatus)
208	apiGroup.POST("/input", s.handleInput)
209	apiGroup.POST("/cancel", s.handleCancel)
210
211	debugAPIServer := &http.Server{
212		Handler: api,
213		// Use some hardcoded numberes that are suitable for testing
214		ReadTimeout:    30 * time.Second, //nolint:gomnd
215		WriteTimeout:   30 * time.Second, //nolint:gomnd
216		MaxHeaderBytes: 1 << 20,          //nolint:gomnd
217	}
218	listener, err := net.Listen("tcp", s.addr)
219	if err != nil {
220		return tracerr.Wrap(err)
221	}
222	log.Infof("prover.MockServer is ready at %v", s.addr)
223	go func() {
224		if err := debugAPIServer.Serve(listener); err != nil &&
225			tracerr.Unwrap(err) != http.ErrServerClosed {
226			log.Fatalf("Listen: %s\n", err)
227		}
228	}()
229	s.wg.Add(1)
230	go func() {
231		s.runProver(ctx)
232		s.wg.Done()
233	}()
234
235	<-ctx.Done()
236	log.Info("Stopping prover.MockServer...")
237
238	s.wg.Wait()
239	ctxTimeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) //nolint:gomnd
240	defer cancel()
241	if err := debugAPIServer.Shutdown(ctxTimeout); err != nil {
242		return tracerr.Wrap(err)
243	}
244	log.Info("prover.MockServer done")
245	return nil
246}