Skip to content

Commit

Permalink
Support for poet certificates cont
Browse files Browse the repository at this point in the history
  • Loading branch information
poszu committed Nov 3, 2023
1 parent 1143849 commit af7311b
Show file tree
Hide file tree
Showing 23 changed files with 589 additions and 177 deletions.
25 changes: 17 additions & 8 deletions activation/activation.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,14 @@ type Builder struct {
smeshingMutex sync.Mutex

// pendingATX is created with current commitment and nipost from current challenge.
pendingATX *types.ActivationTx
layerClock layerClock
syncer syncer
log log.Logger
parentCtx context.Context
stop context.CancelFunc
pendingATX *types.ActivationTx
layerClock layerClock
syncer syncer
log log.Log
parentCtx context.Context
stop context.CancelFunc

poets []PoetClient
poetCfg PoetConfig
poetRetryInterval time.Duration
}
Expand Down Expand Up @@ -125,6 +127,12 @@ func WithPoetConfig(c PoetConfig) BuilderOption {
}
}

func WithPoets(poets ...PoetClient) BuilderOption {
return func(b *Builder) {
b.poets = poets
}
}

func WithValidator(v nipostValidator) BuilderOption {
return func(b *Builder) {
b.validator = v
Expand Down Expand Up @@ -314,8 +322,9 @@ func (b *Builder) run(ctx context.Context) {
for {
err := b.generateInitialPost(ctx)
if err == nil {
// TODO certify initial post
// b.certifier.CertifyAll()
client := NewCertifierClient(b.log.Zap(), b.initialPost, b.initialPostInfo)
b.certifier = NewCertifier(b.nipostBuilder.DataDir(), b.log.Zap(), client)
b.certifier.CertifyAll(ctx, b.poets)
break
}
b.log.Error("Failed to generate initial proof: %s", err)
Expand Down
2 changes: 1 addition & 1 deletion activation/activation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) {
var last time.Time
builderConfirmation := make(chan struct{})
tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Times(expectedTries).DoAndReturn(
func(_ context.Context, challenge *types.NIPostChallenge) (*types.NIPost, error) {
func(_ context.Context, challenge *types.NIPostChallenge, _ certifierService) (*types.NIPost, error) {
now := time.Now()
if now.Sub(last) < retryInterval {
require.FailNow(t, "retry interval not respected")
Expand Down
61 changes: 38 additions & 23 deletions activation/certifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ import (
"github.com/hashicorp/go-retryablehttp"
"github.com/natefinch/atomic"
"github.com/sourcegraph/conc/iter"
"github.com/spacemeshos/go-spacemesh/common/types"
"github.com/spacemeshos/post/shared"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

"github.com/spacemeshos/go-spacemesh/common/types"
)

type ProofToCertify struct {
Expand Down Expand Up @@ -49,28 +50,17 @@ type CertifyResponse struct {
}

type Certifier struct {
client *retryablehttp.Client
logger *zap.Logger
store *certificateStore

post *types.Post
postInfo *types.PostInfo
client certifierClient
}

func NewCertifier(datadir string, logger *zap.Logger, post *types.Post, postInfo *types.PostInfo) *Certifier {
c := &Certifier{
client: retryablehttp.NewClient(),
logger: logger,
store: openCertificateStore(datadir, logger),
post: post,
postInfo: postInfo,
}
c.client.Logger = &retryableHttpLogger{logger}
c.client.ResponseLogHook = func(logger retryablehttp.Logger, resp *http.Response) {
c.logger.Info("response received", zap.Stringer("url", resp.Request.URL), zap.Int("status", resp.StatusCode))
func NewCertifier(datadir string, logger *zap.Logger, client certifierClient) *Certifier {
return &Certifier{
client: client,
logger: logger,
store: openCertificateStore(datadir, logger),
}

return c
}

func (c *Certifier) GetCertificate(poet string) *PoetCert {
Expand All @@ -85,11 +75,14 @@ func (c *Certifier) Recertify(ctx context.Context, poet PoetClient) (*PoetCert,
if err != nil {
return nil, fmt.Errorf("querying certifier info: %w", err)
}
cert, err := c.certifyPost(ctx, info.URL, info.PubKey)
cert, err := c.client.Certify(ctx, info.URL, info.PubKey)
if err != nil {
return nil, fmt.Errorf("certifying POST for %s at %v: %w", poet.Address(), info.URL, info.PubKey)
return nil, fmt.Errorf("certifying POST for %s at %v: %w", poet.Address(), info.URL, err)
}
c.store.put(poet.Address(), *cert)
if err := c.store.persist(); err != nil {
c.logger.Warn("failed to persist poet certs", zap.Error(err))
}

return cert, nil
}
Expand Down Expand Up @@ -162,13 +155,13 @@ func (c *Certifier) CertifyAll(ctx context.Context, poets []PoetClient) map[stri
})),
)

cert, err := c.certifyPost(ctx, svc.URL, svc.PubKey)
cert, err := c.client.Certify(ctx, svc.URL, svc.PubKey)
if err != nil {
c.logger.Warn("failed to certify", zap.Error(err), zap.Stringer("certifier", svc.URL))
continue
}
c.logger.Info(
"sucessfully obtained certificate",
"successfully obtained certificate",
zap.Stringer("certifier", svc.URL),
zap.Binary("cert", cert.Signature),
)
Expand All @@ -181,7 +174,29 @@ func (c *Certifier) CertifyAll(ctx context.Context, poets []PoetClient) map[stri
return certs
}

func (c *Certifier) certifyPost(ctx context.Context, url *url.URL, pubkey []byte) (*PoetCert, error) {
type CertifierClient struct {
client *retryablehttp.Client
post *types.Post
postInfo *types.PostInfo
logger *zap.Logger
}

func NewCertifierClient(logger *zap.Logger, post *types.Post, postInfo *types.PostInfo) *CertifierClient {
c := &CertifierClient{
client: retryablehttp.NewClient(),
logger: logger,
post: post,
postInfo: postInfo,
}
c.client.Logger = &retryableHttpLogger{logger}
c.client.ResponseLogHook = func(logger retryablehttp.Logger, resp *http.Response) {
c.logger.Info("response received", zap.Stringer("url", resp.Request.URL), zap.Int("status", resp.StatusCode))
}

return c
}

func (c *CertifierClient) Certify(ctx context.Context, url *url.URL, pubkey []byte) (*PoetCert, error) {
request := CertifyRequest{
Proof: ProofToCertify{
Pow: c.post.Pow,
Expand Down
48 changes: 48 additions & 0 deletions activation/certifier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package activation_test

import (
"context"
"net/url"
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"go.uber.org/zap/zaptest"

"github.com/spacemeshos/go-spacemesh/activation"
)

func TestPersistsCerts(t *testing.T) {
client := activation.NewMockcertifierClient(gomock.NewController(t))
datadir := t.TempDir()

{
certifier := activation.NewCertifier(datadir, zaptest.NewLogger(t), client)

poetMock := activation.NewMockPoetClient(gomock.NewController(t))
poetMock.EXPECT().Address().Return("http://poet")
poetMock.EXPECT().CertifierInfo(gomock.Any()).Return(&activation.CertifierInfo{
URL: &url.URL{Scheme: "http", Host: "certifier.org"},
PubKey: []byte("pubkey"),
}, nil)

client.EXPECT().
Certify(gomock.Any(), &url.URL{Scheme: "http", Host: "certifier.org"}, []byte("pubkey")).
Return(&activation.PoetCert{Signature: []byte("cert")}, nil)

require.Nil(t, certifier.GetCertificate("http://poet"))
certs, err := certifier.Recertify(context.Background(), poetMock)
require.NoError(t, err)
require.Equal(t, []byte("cert"), certs.Signature)

cert := certifier.GetCertificate("http://poet")
require.Equal(t, []byte("cert"), cert.Signature)
require.Nil(t, certifier.GetCertificate("http://other-poet"))
}
{
// Create new certifier and check that it loads the certs back.
certifier := activation.NewCertifier(datadir, zaptest.NewLogger(t), client)
cert := certifier.GetCertificate("http://poet")
require.Equal(t, []byte("cert"), cert.Signature)
}
}
9 changes: 5 additions & 4 deletions activation/e2e/certifier_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func TestCertification(t *testing.T) {
pubKey, addr := spawnTestCertifier(t, cfg, verifying.WithLabelScryptParams(opts.Scrypt))
certifierCfg := &registration.CertifierConfig{
URL: "http://" + addr.String(),
PubKey: pubKey,
PubKey: registration.Base64Enc(pubKey),
}

for i := 0; i < 2; i++ {
Expand All @@ -78,7 +78,7 @@ func TestCertification(t *testing.T) {
pubKey, addr = spawnTestCertifier(t, cfg, verifying.WithLabelScryptParams(opts.Scrypt))
certifierCfg = &registration.CertifierConfig{
URL: "http://" + addr.String(),
PubKey: pubKey,
PubKey: registration.Base64Enc(pubKey),
}

poet := spawnPoet(t, WithCertifier(certifierCfg))
Expand All @@ -92,8 +92,9 @@ func TestCertification(t *testing.T) {
require.NoError(t, err)
poets = append(poets, client)

certifierClient := activation.NewCertifier(t.TempDir(), zaptest.NewLogger(t), post, info)
certs := certifierClient.CertifyAll(context.Background(), poets)
certifierClient := activation.NewCertifierClient(zaptest.NewLogger(t), post, info)
certifier := activation.NewCertifier(t.TempDir(), zaptest.NewLogger(t), certifierClient)
certs := certifier.CertifyAll(context.Background(), poets)
require.Len(t, certs, 3)
require.Contains(t, certs, poets[0].Address())
require.Contains(t, certs, poets[1].Address())
Expand Down
55 changes: 4 additions & 51 deletions activation/e2e/nipost_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func TestNIPostBuilderWithClients(t *testing.T) {
pubKey, addr := spawnTestCertifier(t, cfg, verifying.WithLabelScryptParams(opts.Scrypt))
certifierCfg := &registration.CertifierConfig{
URL: "http://" + addr.String(),
PubKey: pubKey,
PubKey: registration.Base64Enc(pubKey),
}

poetProver := spawnPoet(
Expand Down Expand Up @@ -200,18 +200,19 @@ func TestNIPostBuilderWithClients(t *testing.T) {
)
require.NoError(t, err)

certifier := activation.NewCertifier(t.TempDir(), logger, post, info)
certifierClient := activation.NewCertifierClient(zaptest.NewLogger(t), post, info)
certifier := activation.NewCertifier(t.TempDir(), logger, certifierClient)
certifier.CertifyAll(context.Background(), []activation.PoetClient{client})

nb, err := activation.NewNIPostBuilder(
poetDb,
svc,
[]string{poetProver.RestURL().String()},
t.TempDir(),
log.NewFromLog(logger),
sig,
poetCfg,
mclock,
activation.WithPoetClients(client),
)
require.NoError(t, err)

Expand All @@ -233,51 +234,3 @@ func TestNIPostBuilderWithClients(t *testing.T) {
)
require.NoError(t, err)
}

func TestNIPostBuilder_Close(t *testing.T) {
ctrl := gomock.NewController(t)

sig, err := signing.NewEdSigner()
require.NoError(t, err)

logger := zaptest.NewLogger(t)

poetProver := spawnPoet(t, WithGenesis(time.Now()), WithEpochDuration(time.Second))
poetDb := activation.NewMockpoetDbAPI(ctrl)

mclock := activation.NewMocklayerClock(ctrl)
mclock.EXPECT().LayerToTime(gomock.Any()).AnyTimes().DoAndReturn(
func(got types.LayerID) time.Time {
// time.Now() ~= currentLayer
genesis := time.Now().Add(-time.Duration(postGenesisEpoch.FirstLayer()) * layerDuration)
return genesis.Add(layerDuration * time.Duration(got))
},
)

svc := grpcserver.NewPostService(logger)

nb, err := activation.NewNIPostBuilder(
poetDb,
svc,
[]string{poetProver.RestURL().String()},
t.TempDir(),
log.NewFromLog(logger),
sig,
activation.PoetConfig{},
mclock,
)
require.NoError(t, err)

ctx, cancel := context.WithCancel(context.Background())
cancel()
challenge := types.NIPostChallenge{
PublishEpoch: postGenesisEpoch + 2,
}

certifier := activation.NewMockcertifierService(ctrl)
certifier.EXPECT().CertifyAll(gomock.Any(), gomock.Any()).Return(nil)

nipost, err := nb.BuildNIPost(ctx, &challenge, certifier)
require.ErrorIs(t, err, context.Canceled)
require.Nil(t, nipost)
}
7 changes: 5 additions & 2 deletions activation/e2e/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ func TestValidator_Validate(t *testing.T) {
WithPhaseShift(poetCfg.PhaseShift),
WithCycleGap(poetCfg.CycleGap),
)
client, err := activation.NewHTTPPoetClient(poetProver.RestURL().String(), poetCfg)
require.NoError(t, err)

mclock := activation.NewMocklayerClock(ctrl)
mclock.EXPECT().LayerToTime(gomock.Any()).AnyTimes().DoAndReturn(
Expand Down Expand Up @@ -95,12 +97,12 @@ func TestValidator_Validate(t *testing.T) {
nb, err := activation.NewNIPostBuilder(
poetDb,
svc,
[]string{poetProver.RestURL().String()},
t.TempDir(),
logtest.New(t, zapcore.DebugLevel),
sig,
poetCfg,
mclock,
activation.WithPoetClients(client),
)
require.NoError(t, err)

Expand All @@ -109,7 +111,8 @@ func TestValidator_Validate(t *testing.T) {
}
challengeHash := challenge.Hash()

certifier := activation.NewCertifier(t.TempDir(), logger, post, info)
certifierClient := activation.NewCertifierClient(zaptest.NewLogger(t), post, info)
certifier := activation.NewCertifier(t.TempDir(), logger, certifierClient)
nipost, err := nb.BuildNIPost(context.Background(), &challenge, certifier)
require.NoError(t, err)

Expand Down
4 changes: 4 additions & 0 deletions activation/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ type CertifierInfo struct {
PubKey []byte
}

type certifierClient interface {
Certify(ctx context.Context, url *url.URL, pubkey []byte) (*PoetCert, error)
}

// certifierService is used to certify nodeID for registerting in the poet.
type certifierService interface {
// Acquire a certificate for the given poet.
Expand Down
Loading

0 comments on commit af7311b

Please sign in to comment.