Skip to content

Commit 2d031c4

Browse files
authored
Merge pull request #6202 from spacemeshos/backport/1.6/fix-poet-pow-fallback
2 parents 334d6cc + 88813a3 commit 2d031c4

File tree

7 files changed

+147
-54
lines changed

7 files changed

+147
-54
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
See [RELEASE](./RELEASE.md) for workflow instructions.
44

5+
## Release v1.6.7
6+
7+
### Improvements
8+
9+
* [#6197](https://github.com/spacemeshos/go-spacemesh/pull/6197) Fix falling back to poet PoW if recertification
10+
failed after getting a 401 on registering
11+
512
## Release v1.6.6
613

714
### Improvements

activation/certifier.go

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,15 @@ func (c *Certifier) Certificate(
117117
case !errors.Is(err, sql.ErrNotFound):
118118
return nil, fmt.Errorf("getting certificate from DB for: %w", err)
119119
}
120-
return c.Recertify(ctx, id, certifier, pubkey)
120+
cert, err = c.client.Certify(ctx, id, certifier, pubkey)
121+
if err != nil {
122+
return nil, fmt.Errorf("certifying POST at %v: %w", certifier, err)
123+
}
124+
125+
if err := certifierdb.AddCertificate(c.db, id, *cert, pubkey); err != nil {
126+
c.logger.Warn("failed to persist poet cert", zap.Error(err))
127+
}
128+
return cert, nil
121129
})
122130

123131
if err != nil {
@@ -126,21 +134,11 @@ func (c *Certifier) Certificate(
126134
return cert.(*certifierdb.PoetCert), nil
127135
}
128136

129-
func (c *Certifier) Recertify(
130-
ctx context.Context,
131-
id types.NodeID,
132-
certifier *url.URL,
133-
pubkey []byte,
134-
) (*certifierdb.PoetCert, error) {
135-
cert, err := c.client.Certify(ctx, id, certifier, pubkey)
136-
if err != nil {
137-
return nil, fmt.Errorf("certifying POST at %v: %w", certifier, err)
138-
}
139-
140-
if err := certifierdb.AddCertificate(c.db, id, *cert, pubkey); err != nil {
141-
c.logger.Warn("failed to persist poet cert", zap.Error(err))
137+
func (c *Certifier) DeleteCertificate(id types.NodeID, pubkey []byte) error {
138+
if err := certifierdb.DeleteCertificate(c.db, id, pubkey); err != nil {
139+
return err
142140
}
143-
return cert, nil
141+
return nil
144142
}
145143

146144
type CertifierClient struct {

activation/interface.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,7 @@ type certifierService interface {
163163
pubkey []byte,
164164
) (*certifier.PoetCert, error)
165165

166-
Recertify(
167-
ctx context.Context,
168-
id types.NodeID,
169-
certifierAddress *url.URL,
170-
pubkey []byte,
171-
) (*certifier.PoetCert, error)
166+
DeleteCertificate(id types.NodeID, pubkey []byte) error
172167
}
173168

174169
type poetDbAPI interface {

activation/mocks.go

Lines changed: 15 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

activation/poet.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -451,8 +451,7 @@ func (c *poetService) authorize(
451451
}
452452
// Fallback to PoW
453453
// TODO: remove this fallback once we migrate to certificates fully.
454-
455-
logger.Debug("querying for poet pow parameters")
454+
logger.Info("falling back to PoW authorization")
456455
powCtx, cancel := withConditionalTimeout(ctx, c.requestTimeout)
457456
defer cancel()
458457
powParams, err := c.powParams(powCtx)
@@ -480,6 +479,22 @@ func (c *poetService) authorize(
480479
}}, nil
481480
}
482481

482+
func (c *poetService) reauthorize(
483+
ctx context.Context,
484+
id types.NodeID,
485+
challange []byte,
486+
logger *zap.Logger,
487+
) (*PoetAuth, error) {
488+
if c.certifier != nil {
489+
if _, pubkey, err := c.getCertifierInfo(ctx); err == nil {
490+
if err := c.certifier.DeleteCertificate(id, pubkey); err != nil {
491+
return nil, fmt.Errorf("deleting cert: %w", err)
492+
}
493+
}
494+
}
495+
return c.authorize(ctx, id, challange, c.logger)
496+
}
497+
483498
func (c *poetService) Submit(
484499
ctx context.Context,
485500
deadline time.Time,
@@ -508,10 +523,10 @@ func (c *poetService) Submit(
508523
case err == nil:
509524
return round, nil
510525
case errors.Is(err, ErrUnauthorized):
511-
logger.Warn("failed to submit challenge as unathorized - recertifying", zap.Error(err))
512-
auth.PoetCert, err = c.recertify(ctx, nodeID)
526+
logger.Warn("failed to submit challenge as unathorized - authorizing again", zap.Error(err))
527+
auth, err := c.reauthorize(ctx, nodeID, challenge, logger)
513528
if err != nil {
514-
return nil, fmt.Errorf("recertifying: %w", err)
529+
return nil, fmt.Errorf("authorizing: %w", err)
515530
}
516531
return c.client.Submit(submitCtx, deadline, prefix, challenge, signature, nodeID, *auth)
517532
}
@@ -560,17 +575,6 @@ func (c *poetService) Certify(ctx context.Context, id types.NodeID) (*certifier.
560575
return c.certifier.Certificate(ctx, id, url, pubkey)
561576
}
562577

563-
func (c *poetService) recertify(ctx context.Context, id types.NodeID) (*certifier.PoetCert, error) {
564-
if c.certifier == nil {
565-
return nil, errors.New("certifier not configured")
566-
}
567-
url, pubkey, err := c.getCertifierInfo(ctx)
568-
if err != nil {
569-
return nil, err
570-
}
571-
return c.certifier.Recertify(ctx, id, url, pubkey)
572-
}
573-
574578
func (c *poetService) getCertifierInfo(ctx context.Context) (*url.URL, []byte, error) {
575579
info, err := c.certifierInfoCache.get(func() (*certifierInfo, error) {
576580
url, pubkey, err := c.client.CertifierInfo(ctx)

activation/poet_client_test.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package activation
22

33
import (
4+
"bytes"
45
"context"
6+
"errors"
57
"io"
68
"net/http"
79
"net/http/httptest"
@@ -375,8 +377,9 @@ func TestPoetClient_RecertifiesOnAuthFailure(t *testing.T) {
375377
mCertifier.EXPECT().
376378
Certificate(gomock.Any(), sig.NodeID(), certifierAddress, certifierPubKey).
377379
Return(&certifier.PoetCert{Data: []byte("first")}, nil),
380+
mCertifier.EXPECT().DeleteCertificate(sig.NodeID(), certifierPubKey),
378381
mCertifier.EXPECT().
379-
Recertify(gomock.Any(), sig.NodeID(), certifierAddress, certifierPubKey).
382+
Certificate(gomock.Any(), sig.NodeID(), certifierAddress, certifierPubKey).
380383
Return(&certifier.PoetCert{Data: []byte("second")}, nil),
381384
)
382385

@@ -389,6 +392,80 @@ func TestPoetClient_RecertifiesOnAuthFailure(t *testing.T) {
389392
require.Equal(t, 2, submitCount)
390393
}
391394

395+
func TestPoetClient_FallbacksToPowWhenCannotRecertify(t *testing.T) {
396+
t.Parallel()
397+
398+
sig, err := signing.NewEdSigner()
399+
require.NoError(t, err)
400+
401+
certifierAddress := &url.URL{Scheme: "http", Host: "certifier"}
402+
certifierPubKey := []byte("certifier-pubkey")
403+
404+
mux := http.NewServeMux()
405+
infoResp, err := protojson.Marshal(&rpcapi.InfoResponse{
406+
ServicePubkey: []byte("pubkey"),
407+
Certifier: &rpcapi.InfoResponse_Cerifier{
408+
Url: certifierAddress.String(),
409+
Pubkey: certifierPubKey,
410+
},
411+
})
412+
require.NoError(t, err)
413+
mux.HandleFunc("GET /v1/info", func(w http.ResponseWriter, r *http.Request) { w.Write(infoResp) })
414+
415+
powChallenge := []byte("challenge")
416+
powResp, err := protojson.Marshal(&rpcapi.PowParamsResponse{PowParams: &rpcapi.PowParams{Challenge: powChallenge}})
417+
require.NoError(t, err)
418+
mux.HandleFunc("GET /v1/pow_params", func(w http.ResponseWriter, r *http.Request) { w.Write(powResp) })
419+
420+
submitResp, err := protojson.Marshal(&rpcapi.SubmitResponse{})
421+
require.NoError(t, err)
422+
submitCount := 0
423+
mux.HandleFunc("POST /v1/submit", func(w http.ResponseWriter, r *http.Request) {
424+
req := rpcapi.SubmitRequest{}
425+
body, _ := io.ReadAll(r.Body)
426+
protojson.Unmarshal(body, &req)
427+
428+
switch {
429+
case submitCount == 0:
430+
w.WriteHeader(http.StatusUnauthorized)
431+
case submitCount == 1 && req.Certificate == nil && bytes.Equal(req.PowParams.Challenge, powChallenge):
432+
w.Write(submitResp)
433+
default:
434+
w.WriteHeader(http.StatusUnauthorized)
435+
}
436+
submitCount++
437+
})
438+
439+
ts := httptest.NewServer(mux)
440+
defer ts.Close()
441+
442+
server := types.PoetServer{
443+
Address: ts.URL,
444+
Pubkey: types.NewBase64Enc([]byte("pubkey")),
445+
}
446+
cfg := PoetConfig{CertifierInfoCacheTTL: time.Hour}
447+
448+
ctrl := gomock.NewController(t)
449+
mCertifier := NewMockcertifierService(ctrl)
450+
gomock.InOrder(
451+
mCertifier.EXPECT().
452+
Certificate(gomock.Any(), sig.NodeID(), certifierAddress, certifierPubKey).
453+
Return(&certifier.PoetCert{Data: []byte("first")}, nil),
454+
mCertifier.EXPECT().DeleteCertificate(sig.NodeID(), certifierPubKey),
455+
mCertifier.EXPECT().
456+
Certificate(gomock.Any(), sig.NodeID(), certifierAddress, certifierPubKey).
457+
Return(nil, errors.New("cannot recertify")),
458+
)
459+
460+
client, err := NewHTTPPoetClient(server, cfg, withCustomHttpClient(ts.Client()))
461+
require.NoError(t, err)
462+
poet := NewPoetServiceWithClient(nil, client, cfg, zaptest.NewLogger(t), WithCertifier(mCertifier))
463+
464+
_, err = poet.Submit(context.Background(), time.Time{}, nil, nil, types.RandomEdSignature(), sig.NodeID())
465+
require.NoError(t, err)
466+
require.Equal(t, 2, submitCount)
467+
}
468+
392469
func TestPoetService_CachesCertifierInfo(t *testing.T) {
393470
t.Parallel()
394471
type test struct {

sql/localsql/certifier/db.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ func AddCertificate(db sql.Executor, nodeID types.NodeID, cert PoetCert, cerifie
2828
return nil
2929
}
3030

31+
func DeleteCertificate(db sql.Executor, nodeID types.NodeID, certifierID []byte) error {
32+
enc := func(stmt *sql.Statement) {
33+
stmt.BindBytes(1, nodeID.Bytes())
34+
stmt.BindBytes(2, certifierID)
35+
}
36+
if _, err := db.Exec(`
37+
DELETE FROM poet_certificates WHERE node_id = ?1 AND certifier_id = ?2;`, enc, nil,
38+
); err != nil {
39+
return fmt.Errorf("deleting poet certificate for (%s; %x): %w", nodeID.ShortString(), certifierID, err)
40+
}
41+
return nil
42+
}
43+
3144
func Certificate(db sql.Executor, nodeID types.NodeID, certifierID []byte) (*PoetCert, error) {
3245
enc := func(stmt *sql.Statement) {
3346
stmt.BindBytes(1, nodeID.Bytes())

0 commit comments

Comments
 (0)