hermez-node/prover/prover.go
prover/prover.goRaw
1package prover
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "fmt"
8 "io"
9 "mime/multipart"
10 "net/http"
11 "strings"
12 "time"
13
14 "github.com/dghubble/sling"
15 "github.com/hermeznetwork/hermez-node/common"
16 "github.com/hermeznetwork/hermez-node/log"
17 "github.com/hermeznetwork/tracerr"
18)
19
20// Proof TBD this type will be received from the proof server
21type Proof struct {
22}
23
24// Client is the interface to a ServerProof that calculates zk proofs
25type Client interface {
26 // Non-blocking
27 CalculateProof(zkInputs *common.ZKInputs) error
28 // Blocking
29 GetProof(ctx context.Context) (*Proof, error)
30 // Non-Blocking
31 Cancel(ctx context.Context) error
32 // Blocking
33 WaitReady(ctx context.Context) error
34}
35
36// StatusCode is the status string of the ProofServer
37type StatusCode string
38
39const (
40 // StatusCodeAborted means prover is ready to take new proof. Previous
41 // proof was aborted.
42 StatusCodeAborted StatusCode = "aborted"
43 // StatusCodeBusy means prover is busy computing proof.
44 StatusCodeBusy StatusCode = "busy"
45 // StatusCodeFailed means prover is ready to take new proof. Previous
46 // proof failed
47 StatusCodeFailed StatusCode = "failed"
48 // StatusCodeSuccess means prover is ready to take new proof. Previous
49 // proof succeeded
50 StatusCodeSuccess StatusCode = "success"
51 // StatusCodeUnverified means prover is ready to take new proof.
52 // Previous proof was unverified
53 StatusCodeUnverified StatusCode = "unverified"
54 // StatusCodeUninitialized means prover is not initialized
55 StatusCodeUninitialized StatusCode = "uninitialized"
56 // StatusCodeUndefined means prover is in an undefined state. Most
57 // likely is booting up. Keep trying
58 StatusCodeUndefined StatusCode = "undefined"
59 // StatusCodeInitializing means prover is initializing and not ready yet
60 StatusCodeInitializing StatusCode = "initializing"
61 // StatusCodeReady means prover initialized and ready to do first proof
62 StatusCodeReady StatusCode = "ready"
63)
64
65// IsReady returns true when the prover is ready
66func (status StatusCode) IsReady() bool {
67 if status == StatusCodeAborted || status == StatusCodeFailed || status == StatusCodeSuccess ||
68 status == StatusCodeUnverified || status == StatusCodeReady {
69 return true
70 }
71 return false
72}
73
74// IsInitialized returns true when the prover is initialized
75func (status StatusCode) IsInitialized() bool {
76 if status == StatusCodeUninitialized || status == StatusCodeUndefined ||
77 status == StatusCodeInitializing {
78 return false
79 }
80 return true
81}
82
83// Status is the return struct for the status API endpoint
84type Status struct {
85 Status StatusCode `json:"status"`
86 Proof string `json:"proof"`
87 PubData string `json:"pubData"`
88}
89
90// ErrorServer is the return struct for an API error
91type ErrorServer struct {
92 Status StatusCode `json:"status"`
93 Message string `json:"msg"`
94}
95
96// Error message for ErrorServer
97func (e ErrorServer) Error() string {
98 return fmt.Sprintf("server proof status (%v): %v", e.Status, e.Message)
99}
100
101type apiMethod string
102
103const (
104 // GET is an HTTP GET
105 GET apiMethod = "GET"
106 // POST is an HTTP POST with maybe JSON body
107 POST apiMethod = "POST"
108 // POSTFILE is an HTTP POST with a form file
109 POSTFILE apiMethod = "POSTFILE"
110)
111
112// ProofServerClient contains the data related to a ProofServerClient
113type ProofServerClient struct {
114 URL string
115 client *sling.Sling
116}
117
118// NewProofServerClient creates a new ServerProof
119func NewProofServerClient(URL string) *ProofServerClient {
120 if URL[len(URL)-1] != '/' {
121 URL += "/"
122 }
123 client := sling.New().Base(URL)
124 return &ProofServerClient{URL: URL, client: client}
125}
126
127//nolint:unused
128type formFileProvider struct {
129 writer *multipart.Writer
130 body []byte
131}
132
133//nolint:unused
134func newFormFileProvider(payload interface{}) (*formFileProvider, error) {
135 body := new(bytes.Buffer)
136 writer := multipart.NewWriter(body)
137 part, err := writer.CreateFormFile("file", "file.json")
138 if err != nil {
139 return nil, tracerr.Wrap(err)
140 }
141 if err := json.NewEncoder(part).Encode(payload); err != nil {
142 return nil, tracerr.Wrap(err)
143 }
144 if err := writer.Close(); err != nil {
145 return nil, tracerr.Wrap(err)
146 }
147 return &formFileProvider{
148 writer: writer,
149 body: body.Bytes(),
150 }, nil
151}
152
153func (p formFileProvider) ContentType() string {
154 return p.writer.FormDataContentType()
155}
156
157func (p formFileProvider) Body() (io.Reader, error) {
158 return bytes.NewReader(p.body), nil
159}
160
161//nolint:unused
162func (p *ProofServerClient) apiRequest(ctx context.Context, method apiMethod, path string,
163 body interface{}, ret interface{}) error {
164 path = strings.TrimPrefix(path, "/")
165 var errSrv ErrorServer
166 var req *http.Request
167 var err error
168 switch method {
169 case GET:
170 req, err = p.client.New().Get(path).Request()
171 case POST:
172 req, err = p.client.New().Post(path).BodyJSON(body).Request()
173 case POSTFILE:
174 provider, err := newFormFileProvider(body)
175 if err != nil {
176 return tracerr.Wrap(err)
177 }
178 req, err = p.client.New().Post(path).BodyProvider(provider).Request()
179 if err != nil {
180 return tracerr.Wrap(err)
181 }
182 default:
183 return tracerr.Wrap(fmt.Errorf("invalid http method: %v", method))
184 }
185 if err != nil {
186 return tracerr.Wrap(err)
187 }
188 res, err := p.client.Do(req.WithContext(ctx), ret, &errSrv)
189 if err != nil {
190 return tracerr.Wrap(err)
191 }
192 defer res.Body.Close() //nolint:errcheck
193 if !(200 <= res.StatusCode && res.StatusCode < 300) {
194 return tracerr.Wrap(errSrv)
195 }
196 return nil
197}
198
199//nolint:unused
200func (p *ProofServerClient) apiStatus(ctx context.Context) (*Status, error) {
201 var status Status
202 if err := p.apiRequest(ctx, GET, "/status", nil, &status); err != nil {
203 return nil, tracerr.Wrap(err)
204 }
205 return &status, nil
206}
207
208//nolint:unused
209func (p *ProofServerClient) apiCancel(ctx context.Context) error {
210 if err := p.apiRequest(ctx, POST, "/cancel", nil, nil); err != nil {
211 return tracerr.Wrap(err)
212 }
213 return nil
214}
215
216//nolint:unused
217func (p *ProofServerClient) apiInput(ctx context.Context, zkInputs *common.ZKInputs) error {
218 if err := p.apiRequest(ctx, POSTFILE, "/input", zkInputs, nil); err != nil {
219 return tracerr.Wrap(err)
220 }
221 return nil
222}
223
224// CalculateProof sends the *common.ZKInputs to the ServerProof to compute the
225// Proof
226func (p *ProofServerClient) CalculateProof(zkInputs *common.ZKInputs) error {
227 log.Error("TODO")
228 return tracerr.Wrap(common.ErrTODO)
229}
230
231// GetProof retreives the Proof from the ServerProof, blocking until the proof
232// is ready.
233func (p *ProofServerClient) GetProof(ctx context.Context) (*Proof, error) {
234 log.Error("TODO")
235 return nil, tracerr.Wrap(common.ErrTODO)
236}
237
238// Cancel cancels any current proof computation
239func (p *ProofServerClient) Cancel(ctx context.Context) error {
240 log.Error("TODO")
241 return tracerr.Wrap(common.ErrTODO)
242}
243
244// WaitReady waits until the serverProof is ready
245func (p *ProofServerClient) WaitReady(ctx context.Context) error {
246 log.Error("TODO")
247 return tracerr.Wrap(common.ErrTODO)
248}
249
250// MockClient is a mock ServerProof to be used in tests. It doesn't calculate anything
251type MockClient struct {
252}
253
254// CalculateProof sends the *common.ZKInputs to the ServerProof to compute the
255// Proof
256func (p *MockClient) CalculateProof(zkInputs *common.ZKInputs) error {
257 return nil
258}
259
260// GetProof retreives the Proof from the ServerProof
261func (p *MockClient) GetProof(ctx context.Context) (*Proof, error) {
262 // Simulate a delay
263 select {
264 case <-time.After(500 * time.Millisecond): //nolint:gomnd
265 return &Proof{}, nil
266 case <-ctx.Done():
267 return nil, tracerr.Wrap(common.ErrDone)
268 }
269}
270
271// Cancel cancels any current proof computation
272func (p *MockClient) Cancel(ctx context.Context) error {
273 // Simulate a delay
274 select {
275 case <-time.After(80 * time.Millisecond): //nolint:gomnd
276 return nil
277 case <-ctx.Done():
278 return tracerr.Wrap(common.ErrDone)
279 }
280}
281
282// WaitReady waits until the prover is ready
283func (p *MockClient) WaitReady(ctx context.Context) error {
284 // Simulate a delay
285 select {
286 case <-time.After(200 * time.Millisecond): //nolint:gomnd
287 return nil
288 case <-ctx.Done():
289 return tracerr.Wrap(common.ErrDone)
290 }
291}