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.

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