@@ -27,7 +27,9 @@ type PoetProvingServiceClient interface {
27
27
Submit (ctx context.Context , challenge []byte , signature []byte ) (* types.PoetRound , error )
28
28
29
29
// PoetServiceID returns the public key of the PoET proving service.
30
- PoetServiceID (context.Context ) ([]byte , error )
30
+ PoetServiceID (context.Context ) (types.PoetServiceID , error )
31
+
32
+ GetProof (ctx context.Context , roundID string ) (* types.PoetProofMessage , error )
31
33
}
32
34
33
35
func (nb * NIPostBuilder ) load (challenge types.Hash32 ) {
@@ -62,9 +64,8 @@ type NIPostBuilder struct {
62
64
}
63
65
64
66
type poetDbAPI interface {
65
- GetMembershipMap (proofRef types.PoetProofRef ) (map [types.Hash32 ]bool , error )
66
67
GetProof (types.PoetProofRef ) (* types.PoetProof , error )
67
- GetProofRef ( poetID [] byte , roundID string ) ( types.PoetProofRef , error )
68
+ ValidateAndStore ( ctx context. Context , proofMessage * types.PoetProofMessage ) error
68
69
}
69
70
70
71
// NewNIPostBuilder returns a NIPostBuilder.
@@ -129,10 +130,10 @@ func (nb *NIPostBuilder) BuildNIPost(ctx context.Context, challenge *types.PoetC
129
130
130
131
validPoetRequests := make ([]types.PoetRequest , 0 , len (poetRequests ))
131
132
for _ , req := range poetRequests {
132
- if ! bytes .Equal (req .PoetRound .ChallengeHash , challengeHash [:]) {
133
+ if ! bytes .Equal (req .PoetRound .ChallengeHash [:] , challengeHash [:]) {
133
134
nb .log .With ().Info (
134
135
"poet returned invalid challenge hash" ,
135
- log . Binary ( "hash" , req .PoetRound .ChallengeHash ) ,
136
+ req .PoetRound .ChallengeHash ,
136
137
log .String ("poet_id" , hex .EncodeToString (req .PoetServiceID )),
137
138
)
138
139
} else {
@@ -148,17 +149,16 @@ func (nb *NIPostBuilder) BuildNIPost(ctx context.Context, challenge *types.PoetC
148
149
nb .persist ()
149
150
}
150
151
151
- // Phase 1: receive proofs from PoET services
152
+ // Phase 1: query PoET services for proofs
152
153
if nb .state .PoetProofRef == nil {
153
- select {
154
- case <- time .After (time .Until (poetProofDeadline )):
155
- case <- ctx .Done ():
156
- return nil , 0 , ctx .Err ()
154
+ getProofsCtx , cancel := context .WithDeadline (ctx , poetProofDeadline )
155
+ defer cancel ()
156
+ poetProofRef , err := nb .getBestProof (getProofsCtx , challengeHash )
157
+ if err != nil {
158
+ return nil , 0 , & PoetSvcUnstableError {msg : "getBestProof failed" , source : err }
157
159
}
158
- poetProofRef := nb .getBestProof (ctx , challengeHash )
159
160
if poetProofRef == nil {
160
- // Time is up - ATX challenge is expired.
161
- return nil , 0 , ErrPoetProofNotReceived
161
+ return nil , 0 , & PoetSvcUnstableError {source : ErrPoetProofNotReceived }
162
162
}
163
163
nb .state .PoetProofRef = poetProofRef
164
164
nb .persist ()
@@ -194,26 +194,20 @@ func (nb *NIPostBuilder) BuildNIPost(ctx context.Context, challenge *types.PoetC
194
194
}
195
195
196
196
// Submit the challenge to a single PoET.
197
- func submitPoetChallenge (ctx context.Context , logger log. Log , poet PoetProvingServiceClient , challenge []byte , signature []byte ) (* types.PoetRequest , error ) {
197
+ func ( nb * NIPostBuilder ) submitPoetChallenge (ctx context.Context , poet PoetProvingServiceClient , challenge []byte , signature []byte ) (* types.PoetRequest , error ) {
198
198
poetServiceID , err := poet .PoetServiceID (ctx )
199
199
if err != nil {
200
200
return nil , & PoetSvcUnstableError {msg : "failed to get PoET service ID" , source : err }
201
201
}
202
-
203
- logger .With ().Debug ("submitting challenge to poet proving service" ,
204
- log .String ("poet_id" , hex .EncodeToString (poetServiceID )))
202
+ logger := nb .log .WithFields (log .String ("poet_id" , hex .EncodeToString (poetServiceID )))
203
+ logger .Debug ("submitting challenge to poet proving service" )
205
204
206
205
round , err := poet .Submit (ctx , challenge , signature )
207
206
if err != nil {
208
- logger .With ().Error ("failed to submit challenge to poet proving service" ,
209
- log .String ("poet_id" , hex .EncodeToString (poetServiceID )),
210
- log .Err (err ))
211
207
return nil , & PoetSvcUnstableError {msg : "failed to submit challenge to poet service" , source : err }
212
208
}
213
209
214
- logger .With ().Info ("challenge submitted to poet proving service" ,
215
- log .String ("poet_id" , hex .EncodeToString (poetServiceID )),
216
- log .String ("round_id" , round .ID ))
210
+ logger .With ().Info ("challenge submitted to poet proving service" , log .String ("round" , round .ID ))
217
211
218
212
return & types.PoetRequest {
219
213
PoetRound : round ,
@@ -228,7 +222,7 @@ func (nb *NIPostBuilder) submitPoetChallenges(ctx context.Context, challenge []b
228
222
for _ , poetProver := range nb .poetProvers {
229
223
poet := poetProver
230
224
g .Go (func () error {
231
- if poetRequest , err := submitPoetChallenge (ctx , nb . log , poet , challenge , signature ); err == nil {
225
+ if poetRequest , err := nb . submitPoetChallenge (ctx , poet , challenge , signature ); err == nil {
232
226
poetRequestsChannel <- * poetRequest
233
227
} else {
234
228
nb .log .With ().Warning ("failed to submit challenge to PoET" , log .Err (err ))
@@ -246,47 +240,110 @@ func (nb *NIPostBuilder) submitPoetChallenges(ctx context.Context, challenge []b
246
240
return poetRequests
247
241
}
248
242
249
- func (nb * NIPostBuilder ) getBestProof (ctx context.Context , challenge * types.Hash32 ) types.PoetProofRef {
250
- type poetProof struct {
251
- ref types.PoetProofRef
252
- leafCount uint64
243
+ func (nb * NIPostBuilder ) getPoetClient (ctx context.Context , id types.PoetServiceID ) PoetProvingServiceClient {
244
+ for _ , client := range nb .poetProvers {
245
+ if clientId , err := client .PoetServiceID (ctx ); err == nil && bytes .Equal (id , clientId ) {
246
+ return client
247
+ }
253
248
}
254
- var bestProof * poetProof
249
+ return nil
250
+ }
255
251
256
- for _ , poetSubmission := range nb . state . PoetRequests {
257
- ref , err := nb . poetDB . GetProofRef ( poetSubmission . PoetServiceID , poetSubmission . PoetRound . ID )
258
- if err != nil {
259
- continue
252
+ func membersContain ( members [][] byte , challenge * types. Hash32 ) bool {
253
+ for _ , member := range members {
254
+ if bytes . Equal ( member , challenge . Bytes ()) {
255
+ return true
260
256
}
261
- // We are interested only in proofs that we are members of
262
- membership , err := nb .poetDB .GetMembershipMap (ref )
263
- if err != nil {
264
- nb .log .With ().Panic ("failed to fetch membership for poet proof" , log .Binary ("challenge" , challenge [:]))
257
+ }
258
+ return false
259
+ }
260
+
261
+ func (nb * NIPostBuilder ) getProofWithRetry (ctx context.Context , client PoetProvingServiceClient , roundID string , retryInterval time.Duration ) (* types.PoetProofMessage , error ) {
262
+ for {
263
+ proof , err := client .GetProof (ctx , roundID )
264
+ switch {
265
+ case err == nil :
266
+ return proof , nil
267
+ case errors .Is (err , ErrUnavailable ) || errors .Is (err , ErrNotFound ):
268
+ nb .log .With ().Debug ("Proof not found, retrying" , log .Duration ("interval" , retryInterval ))
269
+ select {
270
+ case <- ctx .Done ():
271
+ return nil , fmt .Errorf ("retry was canceled: %w" , ctx .Err ())
272
+ case <- time .After (retryInterval ):
273
+ }
274
+ default :
275
+ return nil , err
265
276
}
266
- if ! membership [* challenge ] {
267
- nb .log .With ().Debug ("poet proof membership doesn't contain the challenge" , log .Binary ("challenge" , challenge [:]))
277
+ }
278
+ }
279
+
280
+ func (nb * NIPostBuilder ) getBestProof (ctx context.Context , challenge * types.Hash32 ) (types.PoetProofRef , error ) {
281
+ proofs := make (chan * types.PoetProofMessage , len (nb .state .PoetRequests ))
282
+
283
+ var eg errgroup.Group
284
+ for _ , r := range nb .state .PoetRequests {
285
+ logger := nb .log .WithFields (log .String ("poet_id" , hex .EncodeToString (r .PoetServiceID )), log .String ("round" , r .PoetRound .ID ))
286
+ client := nb .getPoetClient (ctx , r .PoetServiceID )
287
+ if client == nil {
288
+ logger .Warning ("Poet client not found" )
268
289
continue
269
290
}
270
- proof , err := nb .poetDB .GetProof (ref )
271
- if err != nil {
272
- nb .log .Panic ("Inconsistent state of poetDB. Received poetProofRef which doesn't exist in poetDB." )
273
- }
274
- nb .log .With ().Info ("Got a new PoET proof" , log .Uint64 ("leafCount" , proof .LeafCount ), log .Binary ("ref" , ref ))
291
+ round := r .PoetRound .ID
292
+ // Time to wait before quering for the proof
293
+ // The additional second is an optimization to be nicer to poet
294
+ // and don't accidentially ask it to soon and have to retry.
295
+ waitTime := time .Until (r .PoetRound .End .IntoTime ()) + time .Second
296
+ eg .Go (func () error {
297
+ logger .With ().Info ("Waiting till poet round end" , log .Duration ("wait time" , waitTime ))
298
+ select {
299
+ case <- ctx .Done ():
300
+ logger .With ().Info ("Waiting interrupted" , log .Err (ctx .Err ()))
301
+ return ctx .Err ()
302
+ case <- time .After (waitTime ):
303
+ }
304
+ proof , err := nb .getProofWithRetry (ctx , client , round , time .Second )
305
+ if err != nil {
306
+ logger .With ().Warning ("Failed to get proof from Poet" , log .Err (err ))
307
+ return nil
308
+ }
275
309
276
- if bestProof == nil || bestProof .leafCount < proof .LeafCount {
277
- bestProof = & poetProof {
278
- ref : ref ,
279
- leafCount : proof .LeafCount ,
310
+ if err := nb .poetDB .ValidateAndStore (ctx , proof ); err != nil && ! errors .Is (err , ErrObjectExists ) {
311
+ logger .With ().Warning ("Failed to validate and store proof" , log .Err (err ), log .Object ("proof" , proof ))
312
+ return nil
280
313
}
314
+
315
+ // We are interested only in proofs that we are members of
316
+ if ! membersContain (proof .Members , challenge ) {
317
+ logger .With ().Warning ("poet proof membership doesn't contain the challenge" , challenge )
318
+ return nil
319
+ }
320
+
321
+ proofs <- proof
322
+ return nil
323
+ })
324
+ }
325
+ if err := eg .Wait (); err != nil {
326
+ return nil , fmt .Errorf ("querying for proofs failed: %w" , err )
327
+ }
328
+ close (proofs )
329
+
330
+ var bestProof * types.PoetProofMessage
331
+
332
+ for proof := range proofs {
333
+ nb .log .With ().Info ("Got a new PoET proof" , log .Uint64 ("leafCount" , proof .LeafCount ))
334
+ if bestProof == nil || bestProof .LeafCount < proof .LeafCount {
335
+ bestProof = proof
281
336
}
282
337
}
283
338
284
339
if bestProof != nil {
285
- nb .log .With ().Debug ("Selected the best PoET proof" ,
286
- log .Uint64 ("leafCount" , bestProof .leafCount ),
287
- log .Binary ("ref" , bestProof .ref ))
288
- return bestProof .ref
340
+ ref , err := bestProof .Ref ()
341
+ if err != nil {
342
+ return nil , fmt .Errorf ("failed to get proof ref: %w" , err )
343
+ }
344
+ nb .log .With ().Info ("Selected the best proof" , log .Uint64 ("leafCount" , bestProof .LeafCount ), log .Binary ("ref" , ref ))
345
+ return ref , nil
289
346
}
290
347
291
- return nil
348
+ return nil , ErrPoetProofNotReceived
292
349
}
0 commit comments