You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
4.1 KiB

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