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.

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