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