arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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}