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.

246 lines
6.4 KiB

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