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/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}