From 141a25422ea3351ec7a89360b7a8b3b375eac646 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:29:54 +0000 Subject: [PATCH 01/26] Refactor NIPostBuilder and activation.Builder for multiple identities. --- activation/activation.go | 160 ++++++++++++++++++------------ activation/activation_test.go | 147 ++++++++++++++------------- activation/e2e/nipost_test.go | 9 +- activation/e2e/validation_test.go | 3 +- activation/interface.go | 7 +- activation/mocks.go | 37 +++---- activation/nipost.go | 55 +++++----- activation/nipost_test.go | 109 +++++++++----------- config/config.go | 2 +- node/node.go | 3 +- 10 files changed, 274 insertions(+), 258 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index ff07abc486..c3b5992149 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -68,7 +68,6 @@ type Config struct { type Builder struct { eg errgroup.Group - signer *signing.EdSigner accountLock sync.RWMutex coinbaseAccount types.Address goldenATXID types.ATXID @@ -80,7 +79,9 @@ type Builder struct { validator nipostValidator // smeshingMutex protects `StartSmeshing` and `StopSmeshing` from concurrent access + // as well as the fields below. smeshingMutex sync.Mutex + signers map[types.NodeID]*signing.EdSigner started bool layerClock layerClock @@ -126,7 +127,6 @@ func WithValidator(v nipostValidator) BuilderOption { // NewBuilder returns an atx builder that will start a routine that will attempt to create an atx upon each new layer. func NewBuilder( conf Config, - signer *signing.EdSigner, cdb *datastore.CachedDB, localDB *localsql.Database, publisher pubsub.Publisher, @@ -138,7 +138,7 @@ func NewBuilder( ) *Builder { b := &Builder{ parentCtx: context.Background(), - signer: signer, + signers: make(map[types.NodeID]*signing.EdSigner), goldenATXID: conf.GoldenATXID, regossipInterval: conf.RegossipInterval, cdb: cdb, @@ -156,6 +156,22 @@ func NewBuilder( return b } +func (b *Builder) Register(sig *signing.EdSigner) { + b.smeshingMutex.Lock() + defer b.smeshingMutex.Unlock() + if _, exists := b.signers[sig.NodeID()]; exists { + b.log.Error("signing key already registered", zap.Stringer("id", sig.NodeID())) + return + } + + b.log.Info("registered signing key", zap.Stringer("id", sig.NodeID())) + b.signers[sig.NodeID()] = sig + + if b.started { + b.startID(b.parentCtx, sig) + } +} + // Smeshing returns true iff atx builder is smeshing. func (b *Builder) Smeshing() bool { b.smeshingMutex.Lock() @@ -182,31 +198,37 @@ func (b *Builder) StartSmeshing(coinbase types.Address) error { ctx, stop := context.WithCancel(b.parentCtx) b.stop = stop + for _, sig := range b.signers { + b.startID(ctx, sig) // check that `sig` is copied here + } + return nil +} + +func (b *Builder) startID(ctx context.Context, sig *signing.EdSigner) { b.eg.Go(func() error { - b.run(ctx) + b.run(ctx, sig) return nil }) - if b.regossipInterval != 0 { - b.eg.Go(func() error { - ticker := time.NewTicker(b.regossipInterval) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - return ctx.Err() - case <-ticker.C: - if err := b.Regossip(ctx); err != nil { - b.log.Warn("failed to re-gossip", zap.Error(err)) - } + if b.regossipInterval == 0 { + return + } + b.eg.Go(func() error { + ticker := time.NewTicker(b.regossipInterval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-ticker.C: + if err := b.Regossip(ctx, sig.NodeID()); err != nil { + b.log.Warn("failed to re-gossip", zap.Error(err)) } } - }) - } - return nil + } + }) } // StopSmeshing stops the atx builder. -// It doesn't wait for the smeshing to stop. func (b *Builder) StopSmeshing(deleteFiles bool) error { b.smeshingMutex.Lock() defer b.smeshingMutex.Unlock() @@ -223,13 +245,15 @@ func (b *Builder) StopSmeshing(deleteFiles bool) error { if !deleteFiles { return nil } - if err := b.nipostBuilder.ResetState(); err != nil { - b.log.Error("failed to delete builder state", zap.Error(err)) - return err - } - if err := nipost.RemoveChallenge(b.localDB, b.signer.NodeID()); err != nil { - b.log.Error("failed to remove nipost challenge", zap.Error(err)) - return err + for _, sig := range b.signers { + if err := b.nipostBuilder.ResetState(sig.NodeID()); err != nil { + b.log.Error("failed to delete builder state", zap.Error(err)) + return err + } + if err := nipost.RemoveChallenge(b.localDB, sig.NodeID()); err != nil { + b.log.Error("failed to remove nipost challenge", zap.Error(err)) + return err + } } return nil default: @@ -239,16 +263,16 @@ func (b *Builder) StopSmeshing(deleteFiles bool) error { // SmesherID returns the ID of the smesher that created this activation. func (b *Builder) SmesherID() types.NodeID { - return b.signer.NodeID() + return types.EmptyNodeID // TODO(mafa): API needs to be changed to support multiple smeshers } -func (b *Builder) buildInitialPost(ctx context.Context) error { +func (b *Builder) buildInitialPost(ctx context.Context, nodeId types.NodeID) error { // Generate the initial POST if we don't have an ATX... - if _, err := b.cdb.GetLastAtx(b.signer.NodeID()); err == nil { + if _, err := b.cdb.GetLastAtx(nodeId); err == nil { return nil } // ...and if we haven't stored an initial post yet. - _, err := nipost.InitialPost(b.localDB, b.signer.NodeID()) + _, err := nipost.InitialPost(b.localDB, nodeId) switch { case err == nil: b.log.Info("load initial post from db") @@ -261,7 +285,7 @@ func (b *Builder) buildInitialPost(ctx context.Context) error { // Create the initial post and save it. startTime := time.Now() - post, postInfo, err := b.nipostBuilder.Proof(ctx, shared.ZeroChallenge) + post, postInfo, err := b.nipostBuilder.Proof(ctx, nodeId, shared.ZeroChallenge) if err != nil { return fmt.Errorf("post execution: %w", err) } @@ -278,14 +302,14 @@ func (b *Builder) buildInitialPost(ctx context.Context) error { CommitmentATX: postInfo.CommitmentATX, VRFNonce: *postInfo.Nonce, } - return nipost.AddInitialPost(b.localDB, b.signer.NodeID(), initialPost) + return nipost.AddInitialPost(b.localDB, nodeId, initialPost) } -func (b *Builder) run(ctx context.Context) { +func (b *Builder) run(ctx context.Context, sig *signing.EdSigner) { defer b.log.Info("atx builder stopped") for { - err := b.buildInitialPost(ctx) + err := b.buildInitialPost(ctx, sig.NodeID()) if err == nil { break } @@ -299,7 +323,7 @@ func (b *Builder) run(ctx context.Context) { } for { - err := b.PublishActivationTx(ctx) + err := b.PublishActivationTx(ctx, sig) if err == nil { continue } else if errors.Is(err, context.Canceled) { @@ -311,10 +335,10 @@ func (b *Builder) run(ctx context.Context) { switch { case errors.Is(err, ErrATXChallengeExpired): b.log.Debug("retrying with new challenge after waiting for a layer") - if err := b.nipostBuilder.ResetState(); err != nil { + if err := b.nipostBuilder.ResetState(sig.NodeID()); err != nil { b.log.Error("failed to reset nipost builder state", zap.Error(err)) } - if err := nipost.RemoveChallenge(b.localDB, b.signer.NodeID()); err != nil { + if err := nipost.RemoveChallenge(b.localDB, sig.NodeID()); err != nil { b.log.Error("failed to discard challenge", zap.Error(err)) } // give node some time to sync in case selecting the positioning ATX caused the challenge to expire @@ -344,7 +368,7 @@ func (b *Builder) run(ctx context.Context) { } } -func (b *Builder) buildNIPostChallenge(ctx context.Context) (*types.NIPostChallenge, error) { +func (b *Builder) buildNIPostChallenge(ctx context.Context, nodeID types.NodeID) (*types.NIPostChallenge, error) { select { case <-ctx.Done(): return nil, ctx.Err() @@ -352,7 +376,7 @@ func (b *Builder) buildNIPostChallenge(ctx context.Context) (*types.NIPostChalle } current := b.layerClock.CurrentLayer().GetEpoch() - challenge, err := nipost.Challenge(b.localDB, b.signer.NodeID()) + challenge, err := nipost.Challenge(b.localDB, nodeID) switch { case errors.Is(err, sql.ErrNotFound): // build new challenge @@ -360,10 +384,10 @@ func (b *Builder) buildNIPostChallenge(ctx context.Context) (*types.NIPostChalle return nil, fmt.Errorf("get nipost challenge: %w", err) case challenge.PublishEpoch < current: // challenge is stale - if err := b.nipostBuilder.ResetState(); err != nil { + if err := b.nipostBuilder.ResetState(nodeID); err != nil { return nil, fmt.Errorf("reset nipost builder state: %w", err) } - if err := nipost.RemoveChallenge(b.localDB, b.signer.NodeID()); err != nil { + if err := nipost.RemoveChallenge(b.localDB, nodeID); err != nil { return nil, fmt.Errorf("remove stale nipost challenge: %w", err) } default: @@ -371,7 +395,7 @@ func (b *Builder) buildNIPostChallenge(ctx context.Context) (*types.NIPostChalle return challenge, nil } - prev, err := b.cdb.GetLastAtx(b.signer.NodeID()) + prev, err := b.cdb.GetLastAtx(nodeID) switch { case err == nil: current = max(current, prev.PublishEpoch) @@ -403,16 +427,16 @@ func (b *Builder) buildNIPostChallenge(ctx context.Context) (*types.NIPostChalle } } - posAtx, err := b.GetPositioningAtx() + posAtx, err := b.GetPositioningAtx(nodeID) if err != nil { return nil, fmt.Errorf("failed to get positioning ATX: %w", err) } - prevAtx, err := b.cdb.GetLastAtx(b.signer.NodeID()) + prevAtx, err := b.cdb.GetLastAtx(nodeID) switch { case errors.Is(err, sql.ErrNotFound): // initial ATX challenge - post, err := nipost.InitialPost(b.localDB, b.signer.NodeID()) + post, err := nipost.InitialPost(b.localDB, nodeID) if err != nil { return nil, fmt.Errorf("get initial post: %w", err) } @@ -440,7 +464,7 @@ func (b *Builder) buildNIPostChallenge(ctx context.Context) (*types.NIPostChalle } } - if err := nipost.AddChallenge(b.localDB, b.signer.NodeID(), challenge); err != nil { + if err := nipost.AddChallenge(b.localDB, nodeID, challenge); err != nil { return nil, fmt.Errorf("add nipost challenge: %w", err) } return challenge, nil @@ -462,8 +486,8 @@ func (b *Builder) Coinbase() types.Address { } // PublishActivationTx attempts to publish an atx, it returns an error if an atx cannot be created. -func (b *Builder) PublishActivationTx(ctx context.Context) error { - challenge, err := b.buildNIPostChallenge(ctx) +func (b *Builder) PublishActivationTx(ctx context.Context, sig *signing.EdSigner) error { + challenge, err := b.buildNIPostChallenge(ctx, sig.NodeID()) if err != nil { return err } @@ -475,7 +499,7 @@ func (b *Builder) PublishActivationTx(ctx context.Context) error { ) ctx, cancel := context.WithDeadline(ctx, b.layerClock.LayerToTime((challenge.TargetEpoch()).FirstLayer())) defer cancel() - atx, err := b.createAtx(ctx, challenge) + atx, err := b.createAtx(ctx, sig, challenge) if err != nil { return fmt.Errorf("create ATX: %w", err) } @@ -495,10 +519,10 @@ func (b *Builder) PublishActivationTx(ctx context.Context) error { } } - if err := b.nipostBuilder.ResetState(); err != nil { + if err := b.nipostBuilder.ResetState(sig.NodeID()); err != nil { return fmt.Errorf("reset nipost builder state: %w", err) } - if err := nipost.RemoveChallenge(b.localDB, b.signer.NodeID()); err != nil { + if err := nipost.RemoveChallenge(b.localDB, sig.NodeID()); err != nil { return fmt.Errorf("discarding challenge after published ATX: %w", err) } events.EmitAtxPublished( @@ -513,10 +537,14 @@ func (b *Builder) poetRoundStart(epoch types.EpochID) time.Time { return b.layerClock.LayerToTime(epoch.FirstLayer()).Add(b.poetCfg.PhaseShift) } -func (b *Builder) createAtx(ctx context.Context, challenge *types.NIPostChallenge) (*types.ActivationTx, error) { +func (b *Builder) createAtx( + ctx context.Context, + sig *signing.EdSigner, + challenge *types.NIPostChallenge, +) (*types.ActivationTx, error) { pubEpoch := challenge.PublishEpoch - nipostState, err := b.nipostBuilder.BuildNIPost(ctx, challenge) + nipostState, err := b.nipostBuilder.BuildNIPost(ctx, sig, challenge) if err != nil { return nil, fmt.Errorf("build NIPost: %w", err) } @@ -542,14 +570,14 @@ func (b *Builder) createAtx(ctx context.Context, challenge *types.NIPostChalleng } var nonce *types.VRFPostIndex - var nodeID *types.NodeID + var atxNodeID *types.NodeID switch { case challenge.PrevATXID == types.EmptyATXID: - nodeID = new(types.NodeID) - *nodeID = b.signer.NodeID() + atxNodeID = new(types.NodeID) + *atxNodeID = sig.NodeID() nonce = &nipostState.VRFNonce default: - oldNonce, err := atxs.VRFNonce(b.cdb, b.signer.NodeID(), challenge.PublishEpoch) + oldNonce, err := atxs.VRFNonce(b.cdb, sig.NodeID(), challenge.PublishEpoch) if err != nil { b.log.Warn("failed to get VRF nonce for ATX", zap.Error(err)) break @@ -566,8 +594,8 @@ func (b *Builder) createAtx(ctx context.Context, challenge *types.NIPostChalleng nipostState.NumUnits, nonce, ) - atx.InnerActivationTx.NodeID = nodeID - if err = SignAndFinalizeAtx(b.signer, atx); err != nil { + atx.InnerActivationTx.NodeID = atxNodeID + if err = SignAndFinalizeAtx(sig, atx); err != nil { return nil, fmt.Errorf("sign atx: %w", err) } return atx, nil @@ -585,8 +613,8 @@ func (b *Builder) broadcast(ctx context.Context, atx *types.ActivationTx) (int, } // GetPositioningAtx returns atx id with the highest tick height. -func (b *Builder) GetPositioningAtx() (types.ATXID, error) { - id, err := atxs.GetIDWithMaxHeight(b.cdb, b.signer.NodeID()) +func (b *Builder) GetPositioningAtx(nodeId types.NodeID) (types.ATXID, error) { + id, err := atxs.GetIDWithMaxHeight(b.cdb, nodeId) if err != nil { if errors.Is(err, sql.ErrNotFound) { b.log.Info("using golden atx as positioning atx") @@ -597,15 +625,15 @@ func (b *Builder) GetPositioningAtx() (types.ATXID, error) { return id, nil } -func (b *Builder) Regossip(ctx context.Context) error { +func (b *Builder) Regossip(ctx context.Context, nodeID types.NodeID) error { epoch := b.layerClock.CurrentLayer().GetEpoch() - atx, err := atxs.GetIDByEpochAndNodeID(b.cdb, epoch, b.signer.NodeID()) + atx, err := atxs.GetIDByEpochAndNodeID(b.cdb, epoch, nodeID) if errors.Is(err, sql.ErrNotFound) { return nil } else if err != nil { return err } - blob, err := atxs.GetBlob(b.cdb, atx[:]) + blob, err := atxs.GetBlob(b.cdb, atx.Bytes()) if err != nil { return fmt.Errorf("get blob %s: %w", atx.ShortString(), err) } @@ -615,7 +643,7 @@ func (b *Builder) Regossip(ctx context.Context) error { if err := b.publisher.Publish(ctx, pubsub.AtxProtocol, blob); err != nil { return fmt.Errorf("republish %s: %w", atx.ShortString(), err) } - b.log.Debug("regossipped atx", log.ZShortStringer("atx", atx)) + b.log.Debug("re-gossipped atx", log.ZShortStringer("atx", atx)) return nil } diff --git a/activation/activation_test.go b/activation/activation_test.go index cad717d16f..12bdcab2e0 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -146,7 +146,6 @@ func newTestBuilder(tb testing.TB, opts ...BuilderOption) *testAtxBuilder { b := NewBuilder( cfg, - tab.sig, tab.cdb, tab.localDb, tab.mpub, @@ -156,6 +155,7 @@ func newTestBuilder(tb testing.TB, opts ...BuilderOption) *testAtxBuilder { lg.Zap(), opts..., ) + b.Register(tab.sig) tab.Builder = b return tab } @@ -212,8 +212,8 @@ func publishAtx( NumUnits: DefaultPostSetupOpts().NumUnits, LabelsPerUnit: DefaultPostConfig().LabelsPerUnit, }, nil).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).DoAndReturn( - func(_ context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, _ *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { *currLayer = currLayer.Add(buildNIPostLayerDuration) return newNIPostWithChallenge(t, challenge.Hash(), []byte("66666")), nil }) @@ -242,9 +242,9 @@ func publishAtx( return nil }) - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) // create and publish ATX - err := tab.PublishActivationTx(context.Background()) + err := tab.PublishActivationTx(context.Background(), tab.sig) return built, err } @@ -273,8 +273,8 @@ func TestBuilder_StartSmeshingCoinbase(t *testing.T) { tab := newTestBuilder(t) coinbase := types.Address{1, 1, 1} - tab.mnipost.EXPECT().Proof(gomock.Any(), shared.ZeroChallenge).DoAndReturn( - func(ctx context.Context, b []byte) (*types.Post, *types.PostInfo, error) { + tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() return nil, nil, ctx.Err() }) @@ -290,15 +290,15 @@ func TestBuilder_StartSmeshingCoinbase(t *testing.T) { // calling StartSmeshing more than once before calling StopSmeshing is an error require.ErrorContains(t, tab.StartSmeshing(coinbase), "already started") - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) require.NoError(t, tab.StopSmeshing(true)) } func TestBuilder_RestartSmeshing(t *testing.T) { getBuilder := func(t *testing.T) *Builder { tab := newTestBuilder(t) - tab.mnipost.EXPECT().Proof(gomock.Any(), shared.ZeroChallenge).AnyTimes().DoAndReturn( - func(ctx context.Context, b []byte) (*types.Post, *types.PostInfo, error) { + tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).AnyTimes().DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() return nil, nil, ctx.Err() }) @@ -351,15 +351,14 @@ func TestBuilder_StopSmeshing_Delete(t *testing.T) { genesis := time.Now().Add(-time.Duration(currLayer) * layerDuration) return genesis.Add(layerDuration * time.Duration(got)) }).AnyTimes() - tab.mnipost.EXPECT(). - BuildNIPost(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, _ *types.NIPostChallenge) (*nipost.NIPostState, error) { + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, _ *signing.EdSigner, _ *types.NIPostChallenge) (*nipost.NIPostState, error) { <-ctx.Done() return nil, ctx.Err() }). AnyTimes() - tab.mnipost.EXPECT().Proof(gomock.Any(), shared.ZeroChallenge).DoAndReturn( - func(ctx context.Context, b []byte) (*types.Post, *types.PostInfo, error) { + tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() return nil, nil, ctx.Err() }).AnyTimes() @@ -379,7 +378,7 @@ func TestBuilder_StopSmeshing_Delete(t *testing.T) { require.NoError(t, err) require.Equal(t, refChallenge, challenge) // challenge still present - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) @@ -387,7 +386,7 @@ func TestBuilder_StopSmeshing_Delete(t *testing.T) { require.ErrorIs(t, err, sql.ErrNotFound) require.Nil(t, challenge) // challenge deleted - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) // no-op @@ -455,8 +454,8 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { return genesis.Add(layerDuration * time.Duration(got)) }).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).Return(nil, ErrATXChallengeExpired) - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, ErrATXChallengeExpired) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -470,7 +469,7 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { // Act & Verify var eg errgroup.Group eg.Go(func() error { - tab.run(ctx) + tab.run(ctx, tab.sig) return nil }) @@ -511,8 +510,8 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { NumUnits: DefaultPostSetupOpts().NumUnits, LabelsPerUnit: DefaultPostConfig().LabelsPerUnit, }, nil).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).DoAndReturn( - func(_ context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, _ *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { currLayer = currLayer.Add(layersPerEpoch) return newNIPostWithChallenge(t, challenge.Hash(), []byte("66666")), nil }) @@ -540,7 +539,7 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { ) // after successful publish, state is cleaned up - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) tab.mpub.EXPECT().Publish(gomock.Any(), pubsub.AtxProtocol, gomock.Any()).DoAndReturn( // second publish succeeds @@ -554,7 +553,7 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { }, ) // create and publish ATX - require.NoError(t, tab.PublishActivationTx(context.Background())) + require.NoError(t, tab.PublishActivationTx(context.Background(), tab.sig)) // state is cleaned up _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) @@ -594,8 +593,8 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi NumUnits: DefaultPostSetupOpts().NumUnits, LabelsPerUnit: DefaultPostConfig().LabelsPerUnit, }, nil).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).DoAndReturn( - func(_ context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, _ *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { currLayer = currLayer.Add(1) return newNIPostWithChallenge(t, challenge.Hash(), []byte("66666")), nil }) @@ -620,7 +619,7 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi require.NoError(t, nipost.AddChallenge(tab.localDb, tab.sig.NodeID(), ch)) - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) tab.mpub.EXPECT().Publish(gomock.Any(), pubsub.AtxProtocol, gomock.Any()).DoAndReturn( // publish succeeds @@ -635,7 +634,7 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi ) // create and publish ATX - require.NoError(t, tab.PublishActivationTx(context.Background())) + require.NoError(t, tab.PublishActivationTx(context.Background(), tab.sig)) // state is cleaned up _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) @@ -665,8 +664,8 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi genesis := time.Now().Add(-time.Duration(currLayer) * layerDuration) return genesis.Add(layerDuration * time.Duration(got)) }).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).DoAndReturn( - func(_ context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, _ *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { currLayer = currLayer.Add(layersPerEpoch) return newNIPostWithChallenge(t, challenge.Hash(), []byte("66666")), nil }) @@ -698,7 +697,7 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi }, ) // create and publish ATX - err = tab.PublishActivationTx(ctx) + err = tab.PublishActivationTx(ctx, tab.sig) require.ErrorIs(t, err, context.Canceled) // publish returning an error will just cause a retry if not canceled require.NotNil(t, built) @@ -719,7 +718,7 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPosAtx)) tab.mclock.EXPECT().CurrentLayer().DoAndReturn(func() types.LayerID { return currLayer }).AnyTimes() - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) built2, err := publishAtx(t, tab, posEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) require.NotNil(t, built2) @@ -797,8 +796,8 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve return genesis.Add(layerDuration * time.Duration(got)) }).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).Return(nil, ErrATXChallengeExpired) - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, ErrATXChallengeExpired) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) ch := make(chan struct{}) tab.mclock.EXPECT().AwaitLayer(currLayer.Add(1)).Do(func(got types.LayerID) <-chan struct{} { @@ -809,7 +808,7 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve ctx, cancel := context.WithCancel(context.Background()) var eg errgroup.Group eg.Go(func() error { - tab.run(ctx) + tab.run(ctx, tab.sig) return nil }) t.Cleanup(func() { @@ -899,8 +898,8 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { LabelsPerUnit: DefaultPostConfig().LabelsPerUnit, }, nil).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()). - DoAndReturn(func(_ context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, _ *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { currentLayer = currentLayer.Add(5) return newNIPostWithChallenge(t, challenge.Hash(), poetBytes), nil }) @@ -932,9 +931,9 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { return nil }) - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) - r.NoError(tab.PublishActivationTx(context.Background())) + r.NoError(tab.PublishActivationTx(context.Background(), tab.sig)) // state is cleaned up _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) @@ -990,8 +989,8 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { LabelsPerUnit: DefaultPostConfig().LabelsPerUnit, }, nil).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()). - DoAndReturn(func(_ context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, _ *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { currentLayer = currentLayer.Add(layersPerEpoch) return newNIPostWithChallenge(t, challenge.Hash(), poetBytes), nil }) @@ -1027,9 +1026,9 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { nipost.Post{Indices: make([]byte, 10)}, )) - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) - r.NoError(tab.PublishActivationTx(context.Background())) + r.NoError(tab.PublishActivationTx(context.Background(), tab.sig)) // state is cleaned up _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) @@ -1056,8 +1055,8 @@ func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { return genesis.Add(layerDuration * time.Duration(got)) }).AnyTimes() nipostErr := fmt.Errorf("NIPost builder error") - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).Return(nil, nipostErr) - require.ErrorIs(t, tab.PublishActivationTx(context.Background()), nipostErr) + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nipostErr) + require.ErrorIs(t, tab.PublishActivationTx(context.Background(), tab.sig), nipostErr) // state is preserved challenge, err := nipost.Challenge(tab.localDB, tab.sig.NodeID()) @@ -1121,7 +1120,7 @@ func TestBuilder_SignAtx(t *testing.T) { challenge := newChallenge(1, prevAtx, prevAtx, types.EpochID(15), nil) nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) atx := newAtx(t, tab.sig, challenge, nipost.NIPost, 100, types.Address{}) - require.NoError(t, SignAndFinalizeAtx(tab.signer, atx)) + require.NoError(t, SignAndFinalizeAtx(tab.sig, atx)) ok := signing.NewEdVerifier().Verify(signing.ATX, tab.sig.NodeID(), atx.SignedBytes(), atx.Signature) require.True(t, ok) @@ -1181,8 +1180,8 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { tries := 0 var last time.Time builderConfirmation := make(chan struct{}) - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).Times(expectedTries).DoAndReturn( - func(_ context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Times(expectedTries).DoAndReturn( + func(_ context.Context, _ *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { now := time.Now() if now.Sub(last) < retryInterval { require.FailNow(t, "retry interval not respected") @@ -1198,7 +1197,7 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { }, ) - tab.mnipost.EXPECT().ResetState().Return(nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) nonce := types.VRFPostIndex(123) commitmentATX := types.RandomATXID() @@ -1228,7 +1227,7 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { defer cancel() var eg errgroup.Group eg.Go(func() error { - tab.run(ctx) + tab.run(ctx, tab.sig) return nil }) t.Cleanup(func() { assert.NoError(t, eg.Wait()) }) @@ -1258,7 +1257,7 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.mnipost.EXPECT().Proof(gomock.Any(), shared.ZeroChallenge).Return( + tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).Return( &types.Post{Indices: make([]byte, 10)}, &types.PostInfo{ CommitmentATX: types.RandomATXID(), @@ -1270,7 +1269,7 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). AnyTimes(). Return(nil) - require.NoError(t, tab.buildInitialPost(context.Background())) + require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) posEpoch := postGenesisEpoch + 1 challenge := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) @@ -1290,12 +1289,12 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { assertLastAtx(require.New(t), tab.sig.NodeID(), types.BytesToHash(poetByte), atx, vPrevAtx, vPrevAtx, layersPerEpoch) // postClient.Proof() should not be called again - require.NoError(t, tab.buildInitialPost(context.Background())) + require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) } func TestBuilder_InitialPostIsPersisted(t *testing.T) { tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.mnipost.EXPECT().Proof(gomock.Any(), shared.ZeroChallenge).Return( + tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).Return( &types.Post{Indices: make([]byte, 10)}, &types.PostInfo{ CommitmentATX: types.RandomATXID(), @@ -1307,10 +1306,10 @@ func TestBuilder_InitialPostIsPersisted(t *testing.T) { Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). AnyTimes(). Return(nil) - require.NoError(t, tab.buildInitialPost(context.Background())) + require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) // postClient.Proof() should not be called again - require.NoError(t, tab.buildInitialPost(context.Background())) + require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) } func TestWaitPositioningAtx(t *testing.T) { @@ -1339,8 +1338,8 @@ func TestWaitPositioningAtx(t *testing.T) { // everything else are stubs that are irrelevant for the test tab.mpostClient.EXPECT().Info(gomock.Any()).Return(&types.PostInfo{}, nil).AnyTimes() - tab.mnipost.EXPECT().ResetState().Return(nil) - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any()).Return(&nipost.NIPostState{}, nil) + tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(&nipost.NIPostState{}, nil) closed := make(chan struct{}) close(closed) tab.mclock.EXPECT().AwaitLayer(types.EpochID(1).FirstLayer()).Return(closed).AnyTimes() @@ -1359,7 +1358,7 @@ func TestWaitPositioningAtx(t *testing.T) { nipost.Post{Indices: make([]byte, 10)}, )) - err := tab.PublishActivationTx(context.Background()) + err := tab.PublishActivationTx(context.Background(), tab.sig) require.NoError(t, err) }) } @@ -1368,30 +1367,30 @@ func TestWaitPositioningAtx(t *testing.T) { func TestRegossip(t *testing.T) { layer := types.LayerID(10) t.Run("not found", func(t *testing.T) { - h := newTestBuilder(t) - h.mclock.EXPECT().CurrentLayer().Return(layer) - require.NoError(t, h.Regossip(context.Background())) + tab := newTestBuilder(t) + tab.mclock.EXPECT().CurrentLayer().Return(layer) + require.NoError(t, tab.Regossip(context.Background(), tab.sig.NodeID())) }) t.Run("success", func(t *testing.T) { - h := newTestBuilder(t) + tab := newTestBuilder(t) atx := newActivationTx(t, - h.signer, 0, types.EmptyATXID, types.EmptyATXID, nil, + tab.sig, 0, types.EmptyATXID, types.EmptyATXID, nil, layer.GetEpoch(), 0, 1, types.Address{}, 1, &types.NIPost{}) - require.NoError(t, atxs.Add(h.cdb.Database, atx)) - blob, err := atxs.GetBlob(h.cdb.Database, atx.ID().Bytes()) + require.NoError(t, atxs.Add(tab.cdb.Database, atx)) + blob, err := atxs.GetBlob(tab.cdb.Database, atx.ID().Bytes()) require.NoError(t, err) - h.mclock.EXPECT().CurrentLayer().Return(layer) + tab.mclock.EXPECT().CurrentLayer().Return(layer) ctx := context.Background() - h.mpub.EXPECT().Publish(ctx, pubsub.AtxProtocol, blob) - require.NoError(t, h.Regossip(ctx)) + tab.mpub.EXPECT().Publish(ctx, pubsub.AtxProtocol, blob) + require.NoError(t, tab.Regossip(ctx, tab.sig.NodeID())) }) t.Run("checkpointed", func(t *testing.T) { - h := newTestBuilder(t) - require.NoError(t, atxs.AddCheckpointed(h.cdb.Database, - &atxs.CheckpointAtx{ID: types.ATXID{1}, Epoch: layer.GetEpoch(), SmesherID: h.sig.NodeID()})) - h.mclock.EXPECT().CurrentLayer().Return(layer) - require.NoError(t, h.Regossip(context.Background())) + tab := newTestBuilder(t) + require.NoError(t, atxs.AddCheckpointed(tab.cdb.Database, + &atxs.CheckpointAtx{ID: types.ATXID{1}, Epoch: layer.GetEpoch(), SmesherID: tab.sig.NodeID()})) + tab.mclock.EXPECT().CurrentLayer().Return(layer) + require.NoError(t, tab.Regossip(context.Background(), tab.sig.NodeID())) }) } diff --git a/activation/e2e/nipost_test.go b/activation/e2e/nipost_test.go index d3cdcbe224..9583abbfe4 100644 --- a/activation/e2e/nipost_test.go +++ b/activation/e2e/nipost_test.go @@ -185,7 +185,6 @@ func TestNIPostBuilderWithClients(t *testing.T) { svc, []types.PoetServer{{Address: poetProver.RestURL().String()}}, logger.Named("nipostBuilder"), - sig, poetCfg, mclock, ) @@ -194,7 +193,7 @@ func TestNIPostBuilderWithClients(t *testing.T) { challenge := types.NIPostChallenge{ PublishEpoch: postGenesisEpoch + 2, } - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) v := activation.NewValidator(poetDb, cfg, opts.Scrypt, verifier) @@ -238,7 +237,6 @@ func TestNIPostBuilder_Close(t *testing.T) { svc, []types.PoetServer{{Address: poetProver.RestURL().String()}}, logger.Named("nipostBuilder"), - sig, activation.PoetConfig{}, mclock, ) @@ -249,7 +247,7 @@ func TestNIPostBuilder_Close(t *testing.T) { } ctx, cancel := context.WithCancel(context.Background()) cancel() - nipost, err := nb.BuildNIPost(ctx, &challenge) + nipost, err := nb.BuildNIPost(ctx, sig, &challenge) require.ErrorIs(t, err, context.Canceled) require.Nil(t, nipost) } @@ -314,7 +312,6 @@ func TestNewNIPostBuilderNotInitialized(t *testing.T) { svc, []types.PoetServer{{Address: poetProver.RestURL().String()}}, logger.Named("nipostBuilder"), - sig, poetCfg, mclock, ) @@ -334,7 +331,7 @@ func TestNewNIPostBuilderNotInitialized(t *testing.T) { challenge := types.NIPostChallenge{ PublishEpoch: postGenesisEpoch + 2, } - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) require.NotNil(t, nipost) diff --git a/activation/e2e/validation_test.go b/activation/e2e/validation_test.go index 75fa952e8d..020e090095 100644 --- a/activation/e2e/validation_test.go +++ b/activation/e2e/validation_test.go @@ -105,13 +105,12 @@ func TestValidator_Validate(t *testing.T) { svc, []types.PoetServer{{Address: poetProver.RestURL().String()}}, logger.Named("nipostBuilder"), - sig, poetCfg, mclock, ) require.NoError(t, err) - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) v := activation.NewValidator(poetDb, cfg, opts.Scrypt, verifier) diff --git a/activation/interface.go b/activation/interface.go index 7d5ae20cc5..c3a1bd81f1 100644 --- a/activation/interface.go +++ b/activation/interface.go @@ -10,6 +10,7 @@ import ( "github.com/spacemeshos/post/verifying" "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/signing" "github.com/spacemeshos/go-spacemesh/sql/localsql/nipost" ) @@ -68,9 +69,9 @@ type layerClock interface { } type nipostBuilder interface { - BuildNIPost(ctx context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) - Proof(ctx context.Context, challenge []byte) (*types.Post, *types.PostInfo, error) - ResetState() error + BuildNIPost(ctx context.Context, sig *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) + Proof(ctx context.Context, nodeID types.NodeID, challenge []byte) (*types.Post, *types.PostInfo, error) + ResetState(types.NodeID) error } type syncer interface { diff --git a/activation/mocks.go b/activation/mocks.go index 167c1d7b23..1ea6a13cc3 100644 --- a/activation/mocks.go +++ b/activation/mocks.go @@ -14,6 +14,7 @@ import ( time "time" types "github.com/spacemeshos/go-spacemesh/common/types" + signing "github.com/spacemeshos/go-spacemesh/signing" nipost "github.com/spacemeshos/go-spacemesh/sql/localsql/nipost" shared "github.com/spacemeshos/post/shared" verifying "github.com/spacemeshos/post/verifying" @@ -731,18 +732,18 @@ func (m *MocknipostBuilder) EXPECT() *MocknipostBuilderMockRecorder { } // BuildNIPost mocks base method. -func (m *MocknipostBuilder) BuildNIPost(ctx context.Context, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { +func (m *MocknipostBuilder) BuildNIPost(ctx context.Context, sig *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BuildNIPost", ctx, challenge) + ret := m.ctrl.Call(m, "BuildNIPost", ctx, sig, challenge) ret0, _ := ret[0].(*nipost.NIPostState) ret1, _ := ret[1].(error) return ret0, ret1 } // BuildNIPost indicates an expected call of BuildNIPost. -func (mr *MocknipostBuilderMockRecorder) BuildNIPost(ctx, challenge any) *nipostBuilderBuildNIPostCall { +func (mr *MocknipostBuilderMockRecorder) BuildNIPost(ctx, sig, challenge any) *nipostBuilderBuildNIPostCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildNIPost", reflect.TypeOf((*MocknipostBuilder)(nil).BuildNIPost), ctx, challenge) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildNIPost", reflect.TypeOf((*MocknipostBuilder)(nil).BuildNIPost), ctx, sig, challenge) return &nipostBuilderBuildNIPostCall{Call: call} } @@ -758,21 +759,21 @@ func (c *nipostBuilderBuildNIPostCall) Return(arg0 *nipost.NIPostState, arg1 err } // Do rewrite *gomock.Call.Do -func (c *nipostBuilderBuildNIPostCall) Do(f func(context.Context, *types.NIPostChallenge) (*nipost.NIPostState, error)) *nipostBuilderBuildNIPostCall { +func (c *nipostBuilderBuildNIPostCall) Do(f func(context.Context, *signing.EdSigner, *types.NIPostChallenge) (*nipost.NIPostState, error)) *nipostBuilderBuildNIPostCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *nipostBuilderBuildNIPostCall) DoAndReturn(f func(context.Context, *types.NIPostChallenge) (*nipost.NIPostState, error)) *nipostBuilderBuildNIPostCall { +func (c *nipostBuilderBuildNIPostCall) DoAndReturn(f func(context.Context, *signing.EdSigner, *types.NIPostChallenge) (*nipost.NIPostState, error)) *nipostBuilderBuildNIPostCall { c.Call = c.Call.DoAndReturn(f) return c } // Proof mocks base method. -func (m *MocknipostBuilder) Proof(ctx context.Context, challenge []byte) (*types.Post, *types.PostInfo, error) { +func (m *MocknipostBuilder) Proof(ctx context.Context, nodeID types.NodeID, challenge []byte) (*types.Post, *types.PostInfo, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Proof", ctx, challenge) + ret := m.ctrl.Call(m, "Proof", ctx, nodeID, challenge) ret0, _ := ret[0].(*types.Post) ret1, _ := ret[1].(*types.PostInfo) ret2, _ := ret[2].(error) @@ -780,9 +781,9 @@ func (m *MocknipostBuilder) Proof(ctx context.Context, challenge []byte) (*types } // Proof indicates an expected call of Proof. -func (mr *MocknipostBuilderMockRecorder) Proof(ctx, challenge any) *nipostBuilderProofCall { +func (mr *MocknipostBuilderMockRecorder) Proof(ctx, nodeID, challenge any) *nipostBuilderProofCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Proof", reflect.TypeOf((*MocknipostBuilder)(nil).Proof), ctx, challenge) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Proof", reflect.TypeOf((*MocknipostBuilder)(nil).Proof), ctx, nodeID, challenge) return &nipostBuilderProofCall{Call: call} } @@ -798,29 +799,29 @@ func (c *nipostBuilderProofCall) Return(arg0 *types.Post, arg1 *types.PostInfo, } // Do rewrite *gomock.Call.Do -func (c *nipostBuilderProofCall) Do(f func(context.Context, []byte) (*types.Post, *types.PostInfo, error)) *nipostBuilderProofCall { +func (c *nipostBuilderProofCall) Do(f func(context.Context, types.NodeID, []byte) (*types.Post, *types.PostInfo, error)) *nipostBuilderProofCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *nipostBuilderProofCall) DoAndReturn(f func(context.Context, []byte) (*types.Post, *types.PostInfo, error)) *nipostBuilderProofCall { +func (c *nipostBuilderProofCall) DoAndReturn(f func(context.Context, types.NodeID, []byte) (*types.Post, *types.PostInfo, error)) *nipostBuilderProofCall { c.Call = c.Call.DoAndReturn(f) return c } // ResetState mocks base method. -func (m *MocknipostBuilder) ResetState() error { +func (m *MocknipostBuilder) ResetState(arg0 types.NodeID) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ResetState") + ret := m.ctrl.Call(m, "ResetState", arg0) ret0, _ := ret[0].(error) return ret0 } // ResetState indicates an expected call of ResetState. -func (mr *MocknipostBuilderMockRecorder) ResetState() *nipostBuilderResetStateCall { +func (mr *MocknipostBuilderMockRecorder) ResetState(arg0 any) *nipostBuilderResetStateCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetState", reflect.TypeOf((*MocknipostBuilder)(nil).ResetState)) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetState", reflect.TypeOf((*MocknipostBuilder)(nil).ResetState), arg0) return &nipostBuilderResetStateCall{Call: call} } @@ -836,13 +837,13 @@ func (c *nipostBuilderResetStateCall) Return(arg0 error) *nipostBuilderResetStat } // Do rewrite *gomock.Call.Do -func (c *nipostBuilderResetStateCall) Do(f func() error) *nipostBuilderResetStateCall { +func (c *nipostBuilderResetStateCall) Do(f func(types.NodeID) error) *nipostBuilderResetStateCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *nipostBuilderResetStateCall) DoAndReturn(f func() error) *nipostBuilderResetStateCall { +func (c *nipostBuilderResetStateCall) DoAndReturn(f func(types.NodeID) error) *nipostBuilderResetStateCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/activation/nipost.go b/activation/nipost.go index fa81130213..93b950ab64 100644 --- a/activation/nipost.go +++ b/activation/nipost.go @@ -45,7 +45,6 @@ const ( // NIPostBuilder holds the required state and dependencies to create Non-Interactive Proofs of Space-Time (NIPost). type NIPostBuilder struct { localDB *localsql.Database - signer *signing.EdSigner poetProvers map[string]poetClient poetDB poetDbAPI @@ -74,7 +73,6 @@ func NewNIPostBuilder( postService postService, poetServers []types.PoetServer, lg *zap.Logger, - signer *signing.EdSigner, poetCfg PoetConfig, layerClock layerClock, opts ...NIPostBuilderOption, @@ -90,7 +88,6 @@ func NewNIPostBuilder( b := &NIPostBuilder{ localDB: db, - signer: signer, poetProvers: poetClients, poetDB: poetDB, @@ -106,21 +103,25 @@ func NewNIPostBuilder( return b, nil } -func (nb *NIPostBuilder) ResetState() error { - if err := nipost.ClearPoetRegistrations(nb.localDB, nb.signer.NodeID()); err != nil { +func (nb *NIPostBuilder) ResetState(nodeId types.NodeID) error { + if err := nipost.ClearPoetRegistrations(nb.localDB, nodeId); err != nil { return fmt.Errorf("clear poet registrations: %w", err) } - if err := nipost.RemoveNIPost(nb.localDB, nb.signer.NodeID()); err != nil { + if err := nipost.RemoveNIPost(nb.localDB, nodeId); err != nil { return fmt.Errorf("remove nipost: %w", err) } return nil } -func (nb *NIPostBuilder) Proof(ctx context.Context, challenge []byte) (*types.Post, *types.PostInfo, error) { +func (nb *NIPostBuilder) Proof( + ctx context.Context, + nodeID types.NodeID, + challenge []byte, +) (*types.Post, *types.PostInfo, error) { started := false retries := 0 for { - client, err := nb.postService.Client(nb.signer.NodeID()) + client, err := nb.postService.Client(nodeID) if err != nil { select { case <-ctx.Done(): @@ -133,7 +134,7 @@ func (nb *NIPostBuilder) Proof(ctx context.Context, challenge []byte) (*types.Po if retries%10 == 0 { // every 20 seconds inform user about lost connection (for remote post service) // TODO(mafa): emit event warning user about lost connection nb.log.Warn("post service not connected - waiting for reconnection", - zap.Stringer("service id", nb.signer.NodeID()), + zap.Stringer("service id", nodeID), zap.Error(err), ) } @@ -165,6 +166,7 @@ func (nb *NIPostBuilder) Proof(ctx context.Context, challenge []byte) (*types.Po // publish a proof - a process that takes about an epoch. func (nb *NIPostBuilder) BuildNIPost( ctx context.Context, + signer *signing.EdSigner, challenge *types.NIPostChallenge, ) (*nipost.NIPostState, error) { logger := nb.log.With(log.ZContext(ctx)) @@ -204,7 +206,7 @@ func (nb *NIPostBuilder) BuildNIPost( ) // Phase 0: Submit challenge to PoET services. - count, err := nipost.PoetRegistrationCount(nb.localDB, nb.signer.NodeID()) // TODO(mafa): when are these cleared? + count, err := nipost.PoetRegistrationCount(nb.localDB, signer.NodeID()) // TODO(mafa): when are these cleared? if err != nil { return nil, fmt.Errorf("failed to get poet registration count: %w", err) } @@ -222,10 +224,10 @@ func (nb *NIPostBuilder) BuildNIPost( submitCtx, cancel := context.WithDeadline(ctx, poetRoundStart) defer cancel() - if err := nb.submitPoetChallenges(submitCtx, poetProofDeadline, challenge.Hash().Bytes()); err != nil { + if err := nb.submitPoetChallenges(submitCtx, signer, poetProofDeadline, challenge.Hash().Bytes()); err != nil { return nil, fmt.Errorf("submitting to poets: %w", err) } - count, err := nipost.PoetRegistrationCount(nb.localDB, nb.signer.NodeID()) + count, err := nipost.PoetRegistrationCount(nb.localDB, signer.NodeID()) if err != nil { return nil, fmt.Errorf("failed to get poet registration count: %w", err) } @@ -235,7 +237,7 @@ func (nb *NIPostBuilder) BuildNIPost( } // Phase 1: query PoET services for proofs - poetProofRef, membership, err := nipost.PoetProofRef(nb.localDB, nb.signer.NodeID()) + poetProofRef, membership, err := nipost.PoetProofRef(nb.localDB, signer.NodeID()) if err != nil && !errors.Is(err, sql.ErrNotFound) { nb.log.Warn("cannot get poet proof ref", zap.Error(err)) } @@ -254,20 +256,20 @@ func (nb *NIPostBuilder) BuildNIPost( } events.EmitPoetWaitProof(challenge.PublishEpoch, challenge.TargetEpoch(), poetRoundEnd) - poetProofRef, membership, err = nb.getBestProof(ctx, challenge.Hash(), challenge.PublishEpoch) + poetProofRef, membership, err = nb.getBestProof(ctx, signer.NodeID(), challenge.Hash(), challenge.PublishEpoch) if err != nil { return nil, &PoetSvcUnstableError{msg: "getBestProof failed", source: err} } if poetProofRef == types.EmptyPoetProofRef { return nil, &PoetSvcUnstableError{source: ErrPoetProofNotReceived} } - if err := nipost.UpdatePoetProofRef(nb.localDB, nb.signer.NodeID(), poetProofRef, membership); err != nil { + if err := nipost.UpdatePoetProofRef(nb.localDB, signer.NodeID(), poetProofRef, membership); err != nil { nb.log.Warn("cannot persist poet proof ref", zap.Error(err)) } } // Phase 2: Post execution. - nipostState, err := nipost.NIPost(nb.localDB, nb.signer.NodeID()) + nipostState, err := nipost.NIPost(nb.localDB, signer.NodeID()) if err != nil && !errors.Is(err, sql.ErrNotFound) { nb.log.Warn("cannot get nipost", zap.Error(err)) } @@ -289,7 +291,7 @@ func (nb *NIPostBuilder) BuildNIPost( nb.log.Info("starting post execution", zap.Binary("challenge", poetProofRef[:])) startTime := time.Now() - proof, postInfo, err := nb.Proof(postCtx, poetProofRef[:]) + proof, postInfo, err := nb.Proof(postCtx, signer.NodeID(), poetProofRef[:]) if err != nil { return nil, fmt.Errorf("failed to generate Post: %w", err) } @@ -310,7 +312,7 @@ func (nb *NIPostBuilder) BuildNIPost( NumUnits: postInfo.NumUnits, VRFNonce: *postInfo.Nonce, } - if err := nipost.AddNIPost(nb.localDB, nb.signer.NodeID(), nipostState); err != nil { + if err := nipost.AddNIPost(nb.localDB, signer.NodeID(), nipostState); err != nil { nb.log.Warn("cannot persist nipost state", zap.Error(err)) } } @@ -331,6 +333,7 @@ func withConditionalTimeout(ctx context.Context, timeout time.Duration) (context // Submit the challenge to a single PoET. func (nb *NIPostBuilder) submitPoetChallenge( ctx context.Context, + nodeID types.NodeID, deadline time.Time, client poetClient, prefix, challenge []byte, @@ -352,7 +355,7 @@ func (nb *NIPostBuilder) submitPoetChallenge( ctx, powParams.Challenge, challenge, - nb.signer.NodeID().Bytes(), + nodeID.Bytes(), powParams.Difficulty, ) metrics.PoetPowDuration.Set(float64(time.Since(startTime).Nanoseconds())) @@ -364,7 +367,7 @@ func (nb *NIPostBuilder) submitPoetChallenge( submitCtx, cancel := withConditionalTimeout(ctx, nb.poetCfg.RequestTimeout) defer cancel() - round, err := client.Submit(submitCtx, deadline, prefix, challenge, signature, nb.signer.NodeID(), PoetPoW{ + round, err := client.Submit(submitCtx, deadline, prefix, challenge, signature, nodeID, PoetPoW{ Nonce: nonce, Params: *powParams, }) @@ -373,7 +376,7 @@ func (nb *NIPostBuilder) submitPoetChallenge( } logger.Info("challenge submitted to poet proving service", zap.String("round", round.ID)) - return nipost.AddPoetRegistration(nb.localDB, nb.signer.NodeID(), nipost.PoETRegistration{ + return nipost.AddPoetRegistration(nb.localDB, nodeID, nipost.PoETRegistration{ ChallengeHash: types.Hash32(challenge), Address: client.Address(), RoundID: round.ID, @@ -384,18 +387,19 @@ func (nb *NIPostBuilder) submitPoetChallenge( // Submit the challenge to all registered PoETs. func (nb *NIPostBuilder) submitPoetChallenges( ctx context.Context, + signer *signing.EdSigner, deadline time.Time, challenge []byte, ) error { - signature := nb.signer.Sign(signing.POET, challenge) - prefix := bytes.Join([][]byte{nb.signer.Prefix(), {byte(signing.POET)}}, nil) + signature := signer.Sign(signing.POET, challenge) + prefix := bytes.Join([][]byte{signer.Prefix(), {byte(signing.POET)}}, nil) g, ctx := errgroup.WithContext(ctx) errChan := make(chan error, len(nb.poetProvers)) for _, poetClient := range nb.poetProvers { client := poetClient g.Go(func() error { - errChan <- nb.submitPoetChallenge(ctx, deadline, client, prefix, challenge, signature) + errChan <- nb.submitPoetChallenge(ctx, signer.NodeID(), deadline, client, prefix, challenge, signature) return nil }) } @@ -442,6 +446,7 @@ func membersContainChallenge(members []types.Member, challenge types.Hash32) (ui func (nb *NIPostBuilder) getBestProof( ctx context.Context, + nodeID types.NodeID, challenge types.Hash32, publishEpoch types.EpochID, ) (types.PoetProofRef, *types.MerkleProof, error) { @@ -449,7 +454,7 @@ func (nb *NIPostBuilder) getBestProof( poet *types.PoetProofMessage membership *types.MerkleProof } - registrations, err := nipost.PoetRegistrations(nb.localDB, nb.signer.NodeID()) + registrations, err := nipost.PoetRegistrations(nb.localDB, nodeID) if err != nil { return types.PoetProofRef{}, nil, fmt.Errorf("getting poet registrations: %w", err) } diff --git a/activation/nipost_test.go b/activation/nipost_test.go index 7fced292f8..b98f72da85 100644 --- a/activation/nipost_test.go +++ b/activation/nipost_test.go @@ -49,7 +49,6 @@ func defaultLayerClockMock(ctrl *gomock.Controller) *MocklayerClock { type testNIPostBuilder struct { *NIPostBuilder - sig *signing.EdSigner observedLogs *observer.ObservedLogs eventSub <-chan events.UserEvent @@ -62,9 +61,6 @@ type testNIPostBuilder struct { } func newTestNIPostBuilder(tb testing.TB) *testNIPostBuilder { - sig, err := signing.NewEdSigner() - require.NoError(tb, err) - observer, observedLogs := observer.New(zapcore.WarnLevel) logger := zap.New(observer) @@ -79,7 +75,6 @@ func newTestNIPostBuilder(tb testing.TB) *testNIPostBuilder { ctrl := gomock.NewController(tb) tnb := &testNIPostBuilder{ - sig: sig, observedLogs: observedLogs, eventSub: sub.Out(), @@ -97,7 +92,6 @@ func newTestNIPostBuilder(tb testing.TB) *testNIPostBuilder { tnb.mPostService, []types.PoetServer{}, tnb.mLogger, - tnb.sig, PoetConfig{}, tnb.mClock, ) @@ -109,12 +103,14 @@ func newTestNIPostBuilder(tb testing.TB) *testNIPostBuilder { func Test_NIPost_PostClientHandling(t *testing.T) { t.Run("connect then complete", func(t *testing.T) { // post client connects, starts post, then completes successfully - tnb := newTestNIPostBuilder(t) + sig, err := signing.NewEdSigner() + require.NoError(t, err) - tnb.mPostService.EXPECT().Client(tnb.sig.NodeID()).Return(tnb.mPostClient, nil) + tnb := newTestNIPostBuilder(t) + tnb.mPostService.EXPECT().Client(sig.NodeID()).Return(tnb.mPostClient, nil) tnb.mPostClient.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(&types.Post{}, &types.PostInfo{}, nil) - nipost, nipostInfo, err := tnb.Proof(context.Background(), shared.ZeroChallenge) + nipost, nipostInfo, err := tnb.Proof(context.Background(), sig.NodeID(), shared.ZeroChallenge) require.NoError(t, err) require.NotNil(t, nipost) require.NotNil(t, nipostInfo) @@ -142,13 +138,15 @@ func Test_NIPost_PostClientHandling(t *testing.T) { t.Run("connect then error", func(t *testing.T) { // post client connects, starts post, then fails with an error that is not a disconnect + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tnb := newTestNIPostBuilder(t) + tnb.mPostService.EXPECT().Client(sig.NodeID()).Return(tnb.mPostClient, nil) expectedErr := errors.New("some error") - - tnb.mPostService.EXPECT().Client(tnb.sig.NodeID()).Return(tnb.mPostClient, nil) tnb.mPostClient.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(nil, nil, expectedErr) - nipost, nipostInfo, err := tnb.Proof(context.Background(), shared.ZeroChallenge) + nipost, nipostInfo, err := tnb.Proof(context.Background(), sig.NodeID(), shared.ZeroChallenge) require.ErrorIs(t, err, expectedErr) require.Nil(t, nipost) require.Nil(t, nipostInfo) @@ -175,14 +173,16 @@ func Test_NIPost_PostClientHandling(t *testing.T) { t.Run("connect, disconnect, reconnect then complete", func(t *testing.T) { // post client connects, starts post, disconnects in between but completes successfully - tnb := newTestNIPostBuilder(t) + sig, err := signing.NewEdSigner() + require.NoError(t, err) - tnb.mPostService.EXPECT().Client(tnb.sig.NodeID()).Return(tnb.mPostClient, nil) + tnb := newTestNIPostBuilder(t) + tnb.mPostService.EXPECT().Client(sig.NodeID()).Return(tnb.mPostClient, nil) tnb.mPostClient.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(nil, nil, ErrPostClientClosed) - tnb.mPostService.EXPECT().Client(tnb.sig.NodeID()).Return(tnb.mPostClient, nil) + tnb.mPostService.EXPECT().Client(sig.NodeID()).Return(tnb.mPostClient, nil) tnb.mPostClient.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(&types.Post{}, &types.PostInfo{}, nil) - nipost, nipostInfo, err := tnb.Proof(context.Background(), shared.ZeroChallenge) + nipost, nipostInfo, err := tnb.Proof(context.Background(), sig.NodeID(), shared.ZeroChallenge) require.NoError(t, err) require.NotNil(t, nipost) require.NotNil(t, nipostInfo) @@ -210,16 +210,18 @@ func Test_NIPost_PostClientHandling(t *testing.T) { t.Run("connect, disconnect, reconnect then error", func(t *testing.T) { // post client connects, starts post, disconnects in between and then fails to complete - tnb := newTestNIPostBuilder(t) + sig, err := signing.NewEdSigner() + require.NoError(t, err) - tnb.mPostService.EXPECT().Client(tnb.sig.NodeID()).Return(tnb.mPostClient, nil) + tnb := newTestNIPostBuilder(t) + tnb.mPostService.EXPECT().Client(sig.NodeID()).Return(tnb.mPostClient, nil) tnb.mPostClient.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(nil, nil, ErrPostClientClosed).Times(1) - tnb.mPostService.EXPECT().Client(tnb.sig.NodeID()).Return(tnb.mPostClient, nil) + tnb.mPostService.EXPECT().Client(sig.NodeID()).Return(tnb.mPostClient, nil) expectedErr := errors.New("some error") tnb.mPostClient.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(nil, nil, expectedErr) - nipost, nipostInfo, err := tnb.Proof(context.Background(), shared.ZeroChallenge) + nipost, nipostInfo, err := tnb.Proof(context.Background(), sig.NodeID(), shared.ZeroChallenge) require.ErrorIs(t, err, expectedErr) require.Nil(t, nipost) require.Nil(t, nipostInfo) @@ -245,17 +247,19 @@ func Test_NIPost_PostClientHandling(t *testing.T) { }) t.Run("repeated connection failure", func(t *testing.T) { - tnb := newTestNIPostBuilder(t) + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tnb := newTestNIPostBuilder(t) ctx, cancel := context.WithCancel(context.Background()) - tnb.mPostService.EXPECT().Client(tnb.sig.NodeID()).Return(nil, ErrPostClientNotConnected).Times(10) - tnb.mPostService.EXPECT().Client(tnb.sig.NodeID()).DoAndReturn( + tnb.mPostService.EXPECT().Client(sig.NodeID()).Return(nil, ErrPostClientNotConnected).Times(10) + tnb.mPostService.EXPECT().Client(sig.NodeID()).DoAndReturn( func(types.NodeID) (PostClient, error) { cancel() return nil, ErrPostClientNotConnected }) - nipost, nipostInfo, err := tnb.Proof(ctx, shared.ZeroChallenge) + nipost, nipostInfo, err := tnb.Proof(ctx, sig.NodeID(), shared.ZeroChallenge) require.ErrorIs(t, err, context.Canceled) require.Nil(t, nipost) require.Nil(t, nipostInfo) @@ -263,7 +267,7 @@ func Test_NIPost_PostClientHandling(t *testing.T) { require.Equal(t, 1, tnb.observedLogs.Len(), "expected 1 log message") require.Equal(t, zapcore.WarnLevel, tnb.observedLogs.All()[0].Level) require.Equal(t, "post service not connected - waiting for reconnection", tnb.observedLogs.All()[0].Message) - require.Equal(t, tnb.sig.NodeID().String(), tnb.observedLogs.All()[0].ContextMap()["service id"]) + require.Equal(t, sig.NodeID().String(), tnb.observedLogs.All()[0].ContextMap()["service id"]) }) } @@ -286,7 +290,6 @@ func Test_NIPostBuilder_ResetState(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, ) @@ -312,7 +315,7 @@ func Test_NIPostBuilder_ResetState(t *testing.T) { VRFNonce: types.VRFPostIndex(1024), }) - err = nb.ResetState() + err = nb.ResetState(sig.NodeID()) require.NoError(t, err) _, err = nipost.NIPost(db, sig.NodeID()) @@ -354,14 +357,13 @@ func Test_NIPostBuilder_WithMocks(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients([]poetClient{poetProvider}), ) require.NoError(t, err) - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) require.NotNil(t, nipost) } @@ -397,14 +399,13 @@ func TestPostSetup(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients([]poetClient{poetProvider}), ) require.NoError(t, err) - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) require.NotNil(t, nipost) } @@ -462,28 +463,26 @@ func TestNIPostBuilder_BuildNIPost(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients([]poetClient{poetProver}), ) require.NoError(t, err) - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) require.NotNil(t, nipost) poetDb = NewMockpoetDbAPI(ctrl) // fail post exec - require.NoError(t, nb.ResetState()) + require.NoError(t, nb.ResetState(sig.NodeID())) nb, err = NewNIPostBuilder( db, poetDb, postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients([]poetClient{poetProver}), @@ -493,7 +492,7 @@ func TestNIPostBuilder_BuildNIPost(t *testing.T) { postClient.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(nil, nil, fmt.Errorf("error")) // check that proof ref is not called again - nipost, err = nb.BuildNIPost(context.Background(), &challenge) + nipost, err = nb.BuildNIPost(context.Background(), sig, &challenge) require.Nil(t, nipost) require.Error(t, err) @@ -504,7 +503,6 @@ func TestNIPostBuilder_BuildNIPost(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients([]poetClient{poetProver}), @@ -519,7 +517,7 @@ func TestNIPostBuilder_BuildNIPost(t *testing.T) { ) // check that proof ref is not called again - nipost, err = nb.BuildNIPost(context.Background(), &challenge) + nipost, err = nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) require.NotNil(t, nipost) } @@ -592,7 +590,6 @@ func TestNIPostBuilder_ManyPoETs_SubmittingChallenge_DeadlineReached(t *testing. postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, poetCfg, mclock, withPoetClients(poets), @@ -600,7 +597,7 @@ func TestNIPostBuilder_ManyPoETs_SubmittingChallenge_DeadlineReached(t *testing. require.NoError(t, err) // Act - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) // Verify @@ -661,7 +658,6 @@ func TestNIPostBuilder_ManyPoETs_AllFinished(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients(poets), @@ -669,7 +665,7 @@ func TestNIPostBuilder_ManyPoETs_AllFinished(t *testing.T) { require.NoError(t, err) // Act - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) // Verify @@ -708,14 +704,13 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, poetCfg, mclock, withPoetClients([]poetClient{poetProver}), ) require.NoError(t, err) - nipst, err := nb.BuildNIPost(context.Background(), &challenge) + nipst, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.ErrorIs(t, err, ErrPoetServiceUnstable) require.Nil(t, nipst) }) @@ -748,13 +743,12 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, poetCfg, mclock, withPoetClients([]poetClient{poetProver}), ) require.NoError(t, err) - nipst, err := nb.BuildNIPost(context.Background(), &challenge) + nipst, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.ErrorIs(t, err, ErrPoetServiceUnstable) require.Nil(t, nipst) }) @@ -773,13 +767,12 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, poetCfg, mclock, withPoetClients([]poetClient{poetProver}), ) require.NoError(t, err) - nipst, err := nb.BuildNIPost(context.Background(), &challenge) + nipst, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.ErrorIs(t, err, ErrPoetProofNotReceived) require.Nil(t, nipst) }) @@ -801,13 +794,12 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, poetCfg, mclock, withPoetClients([]poetClient{poetProver}), ) require.NoError(t, err) - nipst, err := nb.BuildNIPost(context.Background(), &challenge) + nipst, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.ErrorIs(t, err, ErrPoetProofNotReceived) require.Nil(t, nipst) }) @@ -844,7 +836,6 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients([]poetClient{poetProver}), @@ -852,7 +843,7 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { require.NoError(t, err) challenge := types.NIPostChallenge{PublishEpoch: currLayer.GetEpoch()} - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.ErrorIs(t, err, ErrATXChallengeExpired) require.ErrorContains(t, err, "poet round has already started") require.Nil(t, nipost) @@ -876,7 +867,6 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients([]poetClient{poetProver}), @@ -896,7 +886,7 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { }) require.NoError(t, err) - nipost, err := nb.BuildNIPost(context.Background(), challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, challenge) require.ErrorIs(t, err, ErrATXChallengeExpired) require.ErrorContains(t, err, "poet proof for pub epoch") require.Nil(t, nipost) @@ -920,7 +910,6 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, PoetConfig{}, mclock, withPoetClients([]poetClient{poetProver}), @@ -944,7 +933,7 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { err = nipost.UpdatePoetProofRef(db, sig.NodeID(), [32]byte{1, 2, 3}, &types.MerkleProof{}) require.NoError(t, err) - nipost, err := nb.BuildNIPost(context.Background(), challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, challenge) require.ErrorIs(t, err, ErrATXChallengeExpired) require.ErrorContains(t, err, "deadline to publish ATX for pub epoch") require.Nil(t, nipost) @@ -1013,7 +1002,6 @@ func TestNIPoSTBuilder_Continues_After_Interrupted(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, poetCfg, mclock, withPoetClients([]poetClient{poet}), @@ -1021,7 +1009,7 @@ func TestNIPoSTBuilder_Continues_After_Interrupted(t *testing.T) { require.NoError(t, err) // Act - nipost, err := nb.BuildNIPost(buildCtx, &challenge) + nipost, err := nb.BuildNIPost(buildCtx, sig, &challenge) require.ErrorIs(t, err, context.Canceled) require.Nil(t, nipost) @@ -1030,7 +1018,7 @@ func TestNIPoSTBuilder_Continues_After_Interrupted(t *testing.T) { Submit(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.PoetRound{}, nil) - nipost, err = nb.BuildNIPost(context.Background(), &challenge) + nipost, err = nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) // Verify @@ -1164,14 +1152,13 @@ func TestNIPostBuilder_Mainnet_Poet_Workaround(t *testing.T) { postService, []types.PoetServer{}, zaptest.NewLogger(t), - sig, poetCfg, mclock, withPoetClients(poets), ) require.NoError(t, err) - nipost, err := nb.BuildNIPost(context.Background(), &challenge) + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) require.NoError(t, err) require.NotNil(t, nipost) }) diff --git a/config/config.go b/config/config.go index 42da28bb1e..7b4621fd0e 100644 --- a/config/config.go +++ b/config/config.go @@ -129,7 +129,7 @@ type BaseConfig struct { RegossipAtxInterval time.Duration `mapstructure:"regossip-atx-interval"` // ATXGradeDelay is used to grade ATXs for selection in tortoise active set. - // See grading fuction in miner/proposals_builder.go + // See grading function in miner/proposals_builder.go ATXGradeDelay time.Duration `mapstructure:"atx-grade-delay"` // NoMainOverride forces the "nomain" builds to run on the mainnet diff --git a/node/node.go b/node/node.go index 153e406eea..b6fb8db6c6 100644 --- a/node/node.go +++ b/node/node.go @@ -938,7 +938,6 @@ func (app *App) initServices(ctx context.Context) error { grpcPostService.(*grpcserver.PostService), app.Config.PoetServers, app.addLogger(NipostBuilderLogger, lg).Zap(), - app.edSgn, app.Config.POET, app.clock, ) @@ -953,7 +952,6 @@ func (app *App) initServices(ctx context.Context) error { } atxBuilder := activation.NewBuilder( builderConfig, - app.edSgn, app.cachedDB, app.localDB, app.host, @@ -967,6 +965,7 @@ func (app *App) initServices(ctx context.Context) error { activation.WithPoetRetryInterval(app.Config.HARE3.PreroundDelay), activation.WithValidator(app.validator), ) + atxBuilder.Register(app.edSgn) malfeasanceHandler := malfeasance.NewHandler( app.cachedDB, From bc0831d1340b4b2891d5f3f08008e527e4776c99 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:00:58 +0000 Subject: [PATCH 02/26] Use stop func instead of start boolean to check if smeshing --- activation/activation.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index c3b5992149..8b99b252bb 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -66,7 +66,8 @@ type Config struct { // it is responsible for initializing post, receiving poet proof and orchestrating nipst. after which it will // calculate total weight and providing relevant view as proof. type Builder struct { - eg errgroup.Group + parentCtx context.Context + eg errgroup.Group accountLock sync.RWMutex coinbaseAccount types.Address @@ -82,13 +83,11 @@ type Builder struct { // as well as the fields below. smeshingMutex sync.Mutex signers map[types.NodeID]*signing.EdSigner - started bool + stop context.CancelFunc layerClock layerClock syncer syncer log *zap.Logger - parentCtx context.Context - stop context.CancelFunc poetCfg PoetConfig poetRetryInterval time.Duration } @@ -167,7 +166,7 @@ func (b *Builder) Register(sig *signing.EdSigner) { b.log.Info("registered signing key", zap.Stringer("id", sig.NodeID())) b.signers[sig.NodeID()] = sig - if b.started { + if b.stop != nil { b.startID(b.parentCtx, sig) } } @@ -176,7 +175,7 @@ func (b *Builder) Register(sig *signing.EdSigner) { func (b *Builder) Smeshing() bool { b.smeshingMutex.Lock() defer b.smeshingMutex.Unlock() - return b.started + return b.stop != nil } // StartSmeshing is the main entry point of the atx builder. It runs the main @@ -189,10 +188,9 @@ func (b *Builder) StartSmeshing(coinbase types.Address) error { b.smeshingMutex.Lock() defer b.smeshingMutex.Unlock() - if b.started { + if b.stop != nil { return errors.New("already started") } - b.started = true b.coinbaseAccount = coinbase ctx, stop := context.WithCancel(b.parentCtx) @@ -233,13 +231,13 @@ func (b *Builder) StopSmeshing(deleteFiles bool) error { b.smeshingMutex.Lock() defer b.smeshingMutex.Unlock() - if !b.started { + if b.stop == nil { return errors.New("not started") } b.stop() err := b.eg.Wait() - b.started = false + b.stop = nil switch { case err == nil || errors.Is(err, context.Canceled): if !deleteFiles { From 5099a38b6c38030ff5938997953a7c44d30839ad Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:53:58 +0000 Subject: [PATCH 03/26] Add more tests --- activation/nipost.go | 2 +- activation/nipost_test.go | 65 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/activation/nipost.go b/activation/nipost.go index 93b950ab64..c385063ce6 100644 --- a/activation/nipost.go +++ b/activation/nipost.go @@ -206,7 +206,7 @@ func (nb *NIPostBuilder) BuildNIPost( ) // Phase 0: Submit challenge to PoET services. - count, err := nipost.PoetRegistrationCount(nb.localDB, signer.NodeID()) // TODO(mafa): when are these cleared? + count, err := nipost.PoetRegistrationCount(nb.localDB, signer.NodeID()) if err != nil { return nil, fmt.Errorf("failed to get poet registration count: %w", err) } diff --git a/activation/nipost_test.go b/activation/nipost_test.go index b98f72da85..ed35b7c7a6 100644 --- a/activation/nipost_test.go +++ b/activation/nipost_test.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest/observer" + "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/events" @@ -208,6 +209,56 @@ func Test_NIPost_PostClientHandling(t *testing.T) { } }) + t.Run("connect, disconnect, then cancel before reconnect", func(t *testing.T) { + // post client connects, starts post, disconnects in between and proofing is canceled before reconnection + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tnb := newTestNIPostBuilder(t) + + var eg errgroup.Group + eg.Go(func() error { + select { + case e := <-tnb.eventSub: + event := e.Event.GetPostStart() + require.NotNil(t, event, "wrong event type") + require.EqualValues(t, shared.ZeroChallenge, event.Challenge) + case <-time.After(5 * time.Second): + require.Fail(t, "timeout waiting for event") + } + + cancel() + + select { + case e := <-tnb.eventSub: + event := e.Event.GetPostComplete() + require.NotNil(t, event, "wrong event type") + require.Equal(t, true, e.Event.Failure) + require.Equal(t, "Node failed PoST execution.", e.Event.Help) + case <-time.After(5 * time.Second): + require.Fail(t, "timeout waiting for event") + } + return nil + }) + + tnb.mPostService.EXPECT().Client(sig.NodeID()).Return(tnb.mPostClient, nil) + tnb.mPostClient.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(nil, nil, ErrPostClientClosed) + tnb.mPostService.EXPECT().Client(sig.NodeID()).DoAndReturn( + func(types.NodeID) (PostClient, error) { + <-ctx.Done() + return nil, ErrPostClientNotConnected + }) + + nipost, nipostInfo, err := tnb.Proof(ctx, sig.NodeID(), shared.ZeroChallenge) + require.ErrorIs(t, err, context.Canceled) + require.Nil(t, nipost) + require.Nil(t, nipostInfo) + + require.Nil(t, eg.Wait()) + }) + t.Run("connect, disconnect, reconnect then error", func(t *testing.T) { // post client connects, starts post, disconnects in between and then fails to complete sig, err := signing.NewEdSigner() @@ -522,6 +573,20 @@ func TestNIPostBuilder_BuildNIPost(t *testing.T) { require.NotNil(t, nipost) } +func Test_NIPostBuilder_InvalidPoetAddresses(t *testing.T) { + nb, err := NewNIPostBuilder( + nil, + nil, + nil, + []types.PoetServer{{Address: ":invalid"}}, + zaptest.NewLogger(t).Named("nipostBuilder"), + PoetConfig{}, + nil, + ) + require.ErrorContains(t, err, "cannot create poet client") + require.Nil(t, nb) +} + func TestNIPostBuilder_ManyPoETs_SubmittingChallenge_DeadlineReached(t *testing.T) { t.Parallel() // Arrange From 42c3966fd56df0838cb31569a8c66a9bbc0d0001 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Wed, 24 Jan 2024 18:34:06 +0000 Subject: [PATCH 04/26] Remove shorten utility function --- common/types/beacon.go | 6 ++++-- common/types/hashes.go | 20 ++++++++------------ common/types/nodeid.go | 3 ++- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/common/types/beacon.go b/common/types/beacon.go index 43f9bb4320..66444a39f2 100644 --- a/common/types/beacon.go +++ b/common/types/beacon.go @@ -1,6 +1,8 @@ package types import ( + "fmt" + "github.com/spacemeshos/go-spacemesh/common/util" "github.com/spacemeshos/go-spacemesh/log" ) @@ -25,11 +27,11 @@ func (b Beacon) Hex() string { return util.Encode(b[:]) } // doing full logging into a file. func (b Beacon) String() string { return b.Hex() } -// ShortString returns the first 5 characters of the Beacon, usually for logging purposes. +// ShortString returns the first 10 characters of the Beacon, usually for logging purposes. func (b Beacon) ShortString() string { str := b.Hex() l := len(str) - return Shorten(str[min(2, l):], 10) + return fmt.Sprintf("%.10s", str[min(2, l):]) } // Bytes gets the byte representation of the underlying hash. diff --git a/common/types/hashes.go b/common/types/hashes.go index d4554157f1..f0ff52d9d5 100644 --- a/common/types/hashes.go +++ b/common/types/hashes.go @@ -47,10 +47,11 @@ func (h Hash20) String() string { return h.Hex() } -// ShortString returns a the first 5 characters of the hash, for logging purposes. +// ShortString returns a the first 10 characters of the hash, for logging purposes. func (h Hash20) ShortString() string { - l := len(h.Hex()) - return Shorten(h.Hex()[min(2, l):], 10) + str := h.Hex() + l := len(str) + return fmt.Sprintf("%.10s", str[min(2, l):]) } // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, @@ -178,16 +179,11 @@ func (h Hash32) String() string { return h.ShortString() } -// ShortString returns the first 5 characters of the hash, for logging purposes. +// ShortString returns the first 10 characters of the hash, for logging purposes. func (h Hash32) ShortString() string { - l := len(h.Hex()) - return Shorten(h.Hex()[min(2, l):], 10) -} - -// Shorten shortens a string to a specified length. -func Shorten(s string, maxlen int) string { - l := len(s) - return s[:min(maxlen, l)] + str := h.Hex() + l := len(str) + return fmt.Sprintf("%.10s", str[min(2, l):]) } // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, diff --git a/common/types/nodeid.go b/common/types/nodeid.go index ef9bc6f401..ca5614c6f8 100644 --- a/common/types/nodeid.go +++ b/common/types/nodeid.go @@ -2,6 +2,7 @@ package types import ( "encoding/hex" + "fmt" "github.com/spacemeshos/go-scale" @@ -36,7 +37,7 @@ func (id NodeID) Bytes() []byte { // ShortString returns a the first 5 characters of the ID, for logging purposes. func (id NodeID) ShortString() string { - return Shorten(id.String(), 5) + return fmt.Sprintf("%.5s", id.String()) } // Field returns a log field. Implements the LoggableField interface. From a69fb46f2b21774b17660fe467b077f66ba5fd22 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:22:34 +0000 Subject: [PATCH 05/26] Remove comment --- activation/activation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activation/activation.go b/activation/activation.go index 8b99b252bb..e68de4653d 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -197,7 +197,7 @@ func (b *Builder) StartSmeshing(coinbase types.Address) error { b.stop = stop for _, sig := range b.signers { - b.startID(ctx, sig) // check that `sig` is copied here + b.startID(ctx, sig) } return nil } From 102bcfcadeb309ad409645c6b17e8d61dcd0f176 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:33:47 +0100 Subject: [PATCH 06/26] Cleanup tests --- activation/activation_test.go | 232 +++++++++++++---------- activation/handler_test.go | 347 +++++++++++++++++++++++++--------- activation/post_test.go | 25 ++- activation/validation_test.go | 72 ++++++- 4 files changed, 481 insertions(+), 195 deletions(-) diff --git a/activation/activation_test.go b/activation/activation_test.go index 12bdcab2e0..cc443d1f32 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -45,24 +45,7 @@ func TestMain(m *testing.M) { // ========== Helper functions ========== -func newChallenge( - sequence uint64, - prevAtxID, posAtxID types.ATXID, - PublishEpoch types.EpochID, - cATX *types.ATXID, -) types.NIPostChallenge { - return types.NIPostChallenge{ - Sequence: sequence, - PrevATXID: prevAtxID, - PublishEpoch: PublishEpoch, - PositioningATX: posAtxID, - CommitmentATX: cATX, - } -} - func newAtx( - t testing.TB, - sig *signing.EdSigner, challenge types.NIPostChallenge, nipost *types.NIPost, numUnits uint32, @@ -87,8 +70,14 @@ func newActivationTx( numUnits uint32, nipost *types.NIPost, ) *types.VerifiedActivationTx { - challenge := newChallenge(sequence, prevATX, positioningATX, publishEpoch, cATX) - atx := newAtx(t, sig, challenge, nipost, numUnits, coinbase) + challenge := types.NIPostChallenge{ + Sequence: sequence, + PrevATXID: prevATX, + PublishEpoch: publishEpoch, + PositioningATX: positioningATX, + CommitmentATX: cATX, + } + atx := newAtx(challenge, nipost, numUnits, coinbase) if sequence == 0 { nodeID := sig.NodeID() atx.NodeID = &nodeID @@ -160,32 +149,6 @@ func newTestBuilder(tb testing.TB, opts ...BuilderOption) *testAtxBuilder { return tab } -func assertLastAtx( - r *require.Assertions, - nodeID types.NodeID, - poetRef types.Hash32, - newAtx *types.ActivationTx, - posAtx, prevAtx *types.VerifiedActivationTx, - layersPerEpoch uint32, -) { - atx := newAtx - r.Equal(nodeID, atx.SmesherID) - if prevAtx != nil { - r.Equal(prevAtx.Sequence+1, atx.Sequence) - r.Equal(prevAtx.ID(), atx.PrevATXID) - r.Nil(atx.InitialPost) - r.Nil(atx.VRFNonce) - } else { - r.Zero(atx.Sequence) - r.Equal(types.EmptyATXID, atx.PrevATXID) - r.NotNil(atx.InitialPost) - r.NotNil(atx.VRFNonce) - } - r.Equal(posAtx.ID(), atx.PositioningATX) - r.Equal(posAtx.PublishEpoch+1, atx.PublishEpoch) - r.Equal(poetRef, atx.GetPoetProofRef()) -} - func publishAtx( t *testing.T, tab *testAtxBuilder, @@ -248,25 +211,6 @@ func publishAtx( return built, err } -func addPrevAtx(t *testing.T, db sql.Executor, epoch types.EpochID, sig *signing.EdSigner) *types.VerifiedActivationTx { - challenge := types.NIPostChallenge{ - PublishEpoch: epoch, - } - atx := types.NewActivationTx(challenge, types.Address{}, nil, 2, nil) - atx.SetEffectiveNumUnits(2) - return addAtx(t, db, sig, atx) -} - -func addAtx(t *testing.T, db sql.Executor, sig *signing.EdSigner, atx *types.ActivationTx) *types.VerifiedActivationTx { - require.NoError(t, SignAndFinalizeAtx(sig, atx)) - atx.SetEffectiveNumUnits(atx.NumUnits) - atx.SetReceived(time.Now()) - vAtx, err := atx.Verify(0, 1) - require.NoError(t, err) - require.NoError(t, atxs.Add(db, vAtx)) - return vAtx -} - // ========== Tests ========== func TestBuilder_StartSmeshingCoinbase(t *testing.T) { @@ -404,9 +348,15 @@ func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration})) posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() - ch := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) + ch := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - prevAtx := newAtx(t, tab.sig, ch, nipostData.NIPost, 2, types.Address{}) + prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(tab.sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) @@ -438,9 +388,15 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) // current layer is too late to be able to build a nipost on time currLayer := (postGenesisEpoch + 1).FirstLayer() - ch := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, postGenesisEpoch, nil) + ch := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: postGenesisEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - prevAtx := newAtx(t, tab.sig, ch, nipostData.NIPost, 2, types.Address{}) + prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(tab.sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) @@ -484,9 +440,15 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) posEpoch := postGenesisEpoch currLayer := postGenesisEpoch.FirstLayer() - ch := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, postGenesisEpoch, nil) + ch := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: postGenesisEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - prevAtx := newAtx(t, tab.sig, ch, nipostData.NIPost, 2, types.Address{}) + prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(tab.sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) @@ -567,9 +529,15 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi tab := newTestBuilder(t, WithPoetConfig(poetCfg)) posEpoch := postGenesisEpoch currLayer := (postGenesisEpoch + 1).FirstLayer().Add(5) // late for poet round start - challenge := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, postGenesisEpoch, nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: postGenesisEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - prevAtx := newAtx(t, tab.sig, challenge, nipostData.NIPost, posEpoch.Uint32(), types.Address{}) + prevAtx := newAtx(challenge, nipostData.NIPost, posEpoch.Uint32(), types.Address{}) SignAndFinalizeAtx(tab.sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) @@ -645,9 +613,15 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) posEpoch := types.EpochID(2) currLayer := posEpoch.FirstLayer() - ch := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) + ch := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - prevAtx := newAtx(t, tab.sig, ch, nipostData.NIPost, 2, types.Address{}) + prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(tab.sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) @@ -711,8 +685,14 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi // a stale challenge and builds a new NIPost. posEpoch = types.EpochID(4) currLayer = posEpoch.FirstLayer() - ch = newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) - posAtx := newAtx(t, tab.sig, ch, nipostData.NIPost, 2, types.Address{}) + ch = types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } + posAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(tab.sig, posAtx) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) @@ -734,11 +714,17 @@ func TestBuilder_PublishActivationTx_NoPrevATX(t *testing.T) { tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() - challenge := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) otherSigner, err := signing.NewEdSigner() require.NoError(t, err) - posAtx := newAtx(t, otherSigner, challenge, nipostData.NIPost, 2, types.Address{}) + posAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(otherSigner, posAtx) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) @@ -766,11 +752,17 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() - challenge := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) otherSigner, err := signing.NewEdSigner() require.NoError(t, err) - posAtx := newAtx(t, otherSigner, challenge, nipostData.NIPost, 2, types.Address{}) + posAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(otherSigner, posAtx) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) @@ -850,18 +842,30 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { prevAtxPostEpoch := postGenesisEpoch postAtxPubEpoch := postGenesisEpoch - challenge := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, postAtxPubEpoch, nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: postAtxPubEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } poetBytes := []byte("66666") nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetBytes) - posAtx := newAtx(t, otherSigner, challenge, nipostData.NIPost, 2, types.Address{}) + posAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(otherSigner, posAtx) vPosAtx, err := posAtx.Verify(0, 2) r.NoError(err) r.NoError(atxs.Add(tab.cdb, vPosAtx)) - challenge = newChallenge(0, types.EmptyATXID, posAtx.ID(), prevAtxPostEpoch, nil) + challenge = types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: prevAtxPostEpoch, + PositioningATX: posAtx.ID(), + CommitmentATX: nil, + } challenge.InitialPost = initialPost - prevAtx := newAtx(t, tab.sig, challenge, nipostData.NIPost, 2, types.Address{}) + prevAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) prevAtx.InitialPost = initialPost SignAndFinalizeAtx(tab.sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) @@ -950,10 +954,16 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { currentLayer := postGenesisEpoch.FirstLayer().Add(3) posEpoch := postGenesisEpoch - challenge := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } poetBytes := []byte("66666") nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetBytes) - posAtx := newAtx(t, otherSigner, challenge, nipostData.NIPost, 2, types.Address{}) + posAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(otherSigner, posAtx) vPosAtx, err := posAtx.Verify(0, 1) r.NoError(err) @@ -1039,9 +1049,15 @@ func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() - ch := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) + ch := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - posAtx := newAtx(t, tab.sig, ch, nipostData.NIPost, 2, types.Address{}) + posAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(tab.sig, posAtx) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) @@ -1117,9 +1133,15 @@ func TestBuilder_PublishActivationTx_Serialize(t *testing.T) { func TestBuilder_SignAtx(t *testing.T) { tab := newTestBuilder(t) prevAtx := types.ATXID(types.HexToHash32("0x111")) - challenge := newChallenge(1, prevAtx, prevAtx, types.EpochID(15), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevAtx, + PublishEpoch: types.EpochID(15), + PositioningATX: prevAtx, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, tab.sig, challenge, nipost.NIPost, 100, types.Address{}) + atx := newAtx(challenge, nipost.NIPost, 100, types.Address{}) require.NoError(t, SignAndFinalizeAtx(tab.sig, atx)) ok := signing.NewEdVerifier().Verify(signing.ATX, tab.sig.NodeID(), atx.SignedBytes(), atx.Signature) @@ -1146,10 +1168,16 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { WithPoetRetryInterval(retryInterval), ) posEpoch := types.EpochID(0) - challenge := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } poetBytes := []byte("66666") nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetBytes) - prevAtx := newAtx(t, tab.sig, challenge, nipostData.NIPost, 2, types.Address{}) + prevAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) SignAndFinalizeAtx(tab.sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) @@ -1272,10 +1300,16 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) posEpoch := postGenesisEpoch + 1 - challenge := newChallenge(1, types.ATXID{1, 2, 3}, types.ATXID{1, 2, 3}, posEpoch, nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.ATXID{1, 2, 3}, + PublishEpoch: posEpoch, + PositioningATX: types.ATXID{1, 2, 3}, + CommitmentATX: nil, + } poetByte := []byte("66666") nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetByte) - prevAtx := newAtx(t, tab.sig, challenge, nipost.NIPost, 2, types.Address{}) + prevAtx := newAtx(challenge, nipost.NIPost, 2, types.Address{}) SignAndFinalizeAtx(tab.sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) @@ -1286,7 +1320,15 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { atx, err := publishAtx(t, tab, posEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) require.NotNil(t, atx) - assertLastAtx(require.New(t), tab.sig.NodeID(), types.BytesToHash(poetByte), atx, vPrevAtx, vPrevAtx, layersPerEpoch) + + require.Equal(t, tab.sig.NodeID(), atx.SmesherID) + require.Equal(t, vPrevAtx.Sequence+1, atx.Sequence) + require.Equal(t, vPrevAtx.ID(), atx.PrevATXID) + require.Nil(t, atx.InitialPost) + require.Nil(t, atx.VRFNonce) + require.Equal(t, vPrevAtx.ID(), atx.PositioningATX) + require.Equal(t, vPrevAtx.PublishEpoch+1, atx.PublishEpoch) + require.Equal(t, types.BytesToHash(poetByte), atx.GetPoetProofRef()) // postClient.Proof() should not be called again require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) diff --git a/activation/handler_test.go b/activation/handler_test.go index a9d73d09dd..2a39cb8226 100644 --- a/activation/handler_test.go +++ b/activation/handler_test.go @@ -350,8 +350,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(prevAtx.Sequence+1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: prevAtx.Sequence + 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.NIPost = newNIPostWithChallenge(t, atx.NIPostChallenge.Hash(), poetRef).NIPost require.NoError(t, SignAndFinalizeAtx(sig, atx)) @@ -372,9 +378,15 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(prevAtx.Sequence+1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: prevAtx.Sequence + 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } nonce := types.VRFPostIndex(999) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.NIPost = newNIPostWithChallenge(t, atx.NIPostChallenge.Hash(), poetRef).NIPost atx.VRFNonce = &nonce require.NoError(t, SignAndFinalizeAtx(sig, atx)) @@ -400,15 +412,15 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(prevAtx.Sequence+1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx( - t, - sig, - challenge, - &types.NIPost{}, - 90, - types.GenerateAddress([]byte("aaaa")), - ) // numunits decreased from 100 to 90 between atx and prevAtx + challenge := types.NIPostChallenge{ + Sequence: prevAtx.Sequence + 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + // numunits decreased from 100 to 90 between atx and prevAtx + atx := newAtx(challenge, &types.NIPost{}, 90, types.GenerateAddress([]byte("aaaa"))) atx.NIPost = newNIPostWithChallenge(t, atx.NIPostChallenge.Hash(), poetRef).NIPost require.NoError(t, SignAndFinalizeAtx(sig, atx)) @@ -431,15 +443,15 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(prevAtx.Sequence+1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx( - t, - sig, - challenge, - &types.NIPost{}, - 110, - types.GenerateAddress([]byte("aaaa")), - ) // numunits increased from 100 to 110 between atx and prevAtx + challenge := types.NIPostChallenge{ + Sequence: prevAtx.Sequence + 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + // numunits increased from 100 to 110 between atx and prevAtx + atx := newAtx(challenge, &types.NIPost{}, 110, types.GenerateAddress([]byte("aaaa"))) atx.NIPost = newNIPostWithChallenge(t, atx.NIPostChallenge.Hash(), poetRef).NIPost require.NoError(t, SignAndFinalizeAtx(sig, atx)) @@ -465,15 +477,15 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(prevAtx.Sequence+1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx( - t, - sig, - challenge, - &types.NIPost{}, - 110, - types.GenerateAddress([]byte("aaaa")), - ) // numunits increased from 100 to 110 between atx and prevAtx + challenge := types.NIPostChallenge{ + Sequence: prevAtx.Sequence + 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + // numunits increased from 100 to 110 between atx and prevAtx + atx := newAtx(challenge, &types.NIPost{}, 110, types.GenerateAddress([]byte("aaaa"))) atx.NIPost = newNIPostWithChallenge(t, atx.NIPostChallenge.Hash(), poetRef).NIPost require.NoError(t, SignAndFinalizeAtx(sig, atx)) @@ -494,8 +506,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { require.NoError(t, atxs.Add(atxHdlr.cdb, posAtx)) ctxID := posAtx.ID() - challenge := newChallenge(0, types.EmptyATXID, posAtx.ID(), currentLayer.GetEpoch(), &ctxID) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: posAtx.ID(), + CommitmentATX: &ctxID, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.InitialPost = &types.Post{ Nonce: 0, Indices: make([]byte, 10), @@ -529,8 +547,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) - challenge := newChallenge(prevAtx.Sequence+1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch().Add(2), nil) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: prevAtx.Sequence + 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch().Add(2), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.NIPost = newNIPostWithChallenge(t, atx.NIPostChallenge.Hash(), poetRef).NIPost require.NoError(t, SignAndFinalizeAtx(sig, atx)) @@ -545,8 +569,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(prevAtx.Sequence+1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: prevAtx.Sequence + 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) require.NoError(t, SignAndFinalizeAtx(sig, atx)) atxHdlr.mclock.EXPECT().CurrentLayer().Return(currentLayer) @@ -564,8 +594,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(prevAtx.Sequence+1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: prevAtx.Sequence + 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) require.NoError(t, SignAndFinalizeAtx(sig, atx)) atxHdlr.mclock.EXPECT().CurrentLayer().Return(currentLayer) @@ -584,8 +620,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) cATX := posAtx.ID() - challenge := newChallenge(0, types.EmptyATXID, posAtx.ID(), currentLayer.GetEpoch(), &cATX) - atx := newAtx(t, sig, challenge, npst.NIPost, npst.NumUnits, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: posAtx.ID(), + CommitmentATX: &cATX, + } + atx := newAtx(challenge, npst.NIPost, npst.NumUnits, types.GenerateAddress([]byte("aaaa"))) atx.InitialPost = &types.Post{ Nonce: 0, Indices: make([]byte, 10), @@ -613,8 +655,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) ctxID := posAtx.ID() - challenge := newChallenge(0, types.EmptyATXID, posAtx.ID(), currentLayer.GetEpoch(), &ctxID) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: posAtx.ID(), + CommitmentATX: &ctxID, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.InitialPost = &types.Post{ Nonce: 0, Indices: make([]byte, 10), @@ -630,8 +678,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) cATX := posAtx.ID() - challenge := newChallenge(0, types.EmptyATXID, posAtx.ID(), currentLayer.GetEpoch(), &cATX) - atx := newAtx(t, sig, challenge, npst.NIPost, npst.NumUnits, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: posAtx.ID(), + CommitmentATX: &cATX, + } + atx := newAtx(challenge, npst.NIPost, npst.NumUnits, types.GenerateAddress([]byte("aaaa"))) atx.InitialPost = &types.Post{ Nonce: 0, Indices: make([]byte, 10), @@ -650,8 +704,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) ctxID := posAtx.ID() - challenge := newChallenge(0, types.EmptyATXID, posAtx.ID(), currentLayer.GetEpoch(), &ctxID) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: posAtx.ID(), + CommitmentATX: &ctxID, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.InitialPost = &types.Post{ Nonce: 0, Indices: make([]byte, 10), @@ -676,8 +736,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) - challenge := newChallenge(0, types.EmptyATXID, posAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: posAtx.ID(), + CommitmentATX: nil, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.CommitmentATX = &goldenATXID atxHdlr.mclock.EXPECT().CurrentLayer().Return(currentLayer) @@ -691,8 +757,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) cATX := posAtx.ID() - challenge := newChallenge(0, types.EmptyATXID, posAtx.ID(), currentLayer.GetEpoch(), &cATX) - atx := newAtx(t, sig, challenge, npst.NIPost, npst.NumUnits, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: posAtx.ID(), + CommitmentATX: &cATX, + } + atx := newAtx(challenge, npst.NIPost, npst.NumUnits, types.GenerateAddress([]byte("aaaa"))) atx.InitialPost = &types.Post{ Nonce: 0, Indices: make([]byte, 10), @@ -717,8 +789,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.InitialPost = &types.Post{ Nonce: 0, Indices: make([]byte, 10), @@ -736,8 +814,14 @@ func TestHandler_SyntacticallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(1, prevAtx.ID(), prevAtx.ID(), currentLayer.GetEpoch(), nil) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + atx := newAtx(challenge, &types.NIPost{}, 100, types.GenerateAddress([]byte("aaaa"))) atx.NIPost = newNIPostWithChallenge(t, atx.NIPostChallenge.Hash(), poetRef).NIPost atx.InnerActivationTx.NodeID = new(types.NodeID) *atx.InnerActivationTx.NodeID = sig.NodeID() @@ -787,8 +871,14 @@ func TestHandler_ContextuallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) - challenge := newChallenge(1, types.EmptyATXID, goldenATXID, 0, nil) - atx := newAtx(t, sig, challenge, nil, 2, types.Address{}) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.EmptyATXID, + PublishEpoch: 0, + PositioningATX: goldenATXID, + CommitmentATX: nil, + } + atx := newAtx(challenge, nil, 2, types.Address{}) require.NoError(t, SignAndFinalizeAtx(sig, atx)) vAtx, err := atx.Verify(0, 1) @@ -802,15 +892,27 @@ func TestHandler_ContextuallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) require.NoError(t, atxs.Add(atxHdlr.cdb, prevAtx)) - challenge := newChallenge(1, types.EmptyATXID, goldenATXID, currentLayer.GetEpoch(), nil) - atx := newAtx(t, sig, challenge, &types.NIPost{}, 100, coinbase) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.EmptyATXID, + PublishEpoch: currentLayer.GetEpoch(), + PositioningATX: goldenATXID, + CommitmentATX: nil, + } + atx := newAtx(challenge, &types.NIPost{}, 100, coinbase) require.NoError(t, SignAndFinalizeAtx(sig, atx)) vAtx, err := atx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(atxHdlr.cdb, vAtx)) - challenge = newChallenge(1, prevAtx.ID(), goldenATXID, currentLayer.GetEpoch()-1, nil) - atx = newAtx(t, sig, challenge, &types.NIPost{}, 100, coinbase) + challenge = types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevAtx.ID(), + PublishEpoch: currentLayer.GetEpoch() - 1, + PositioningATX: goldenATXID, + CommitmentATX: nil, + } + atx = newAtx(challenge, &types.NIPost{}, 100, coinbase) require.NoError(t, SignAndFinalizeAtx(sig, atx)) vAtx, err = atx.Verify(0, 1) require.NoError(t, err) @@ -824,8 +926,14 @@ func TestHandler_ContextuallyValidateAtx(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) - challenge := newChallenge(1, types.RandomATXID(), prevAtx.ID(), 0, nil) - atx := newAtx(t, sig, challenge, nil, 2, types.Address{}) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.RandomATXID(), + PublishEpoch: 0, + PositioningATX: prevAtx.ID(), + CommitmentATX: nil, + } + atx := newAtx(challenge, nil, 2, types.Address{}) require.NoError(t, SignAndFinalizeAtx(sig, atx)) vAtx, err := atx.Verify(0, 1) require.NoError(t, err) @@ -1157,9 +1265,15 @@ func BenchmarkNewActivationDb(b *testing.B) { eStart := time.Now() for epoch := postGenesisEpoch; epoch < postGenesisEpoch+numOfEpochs; epoch++ { for i := 0; i < numOfMiners; i++ { - challenge := newChallenge(1, prevAtxs[i], posAtx, epoch, nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevAtxs[i], + PublishEpoch: epoch, + PositioningATX: posAtx, + CommitmentATX: nil, + } npst := newNIPostWithChallenge(b, challenge.Hash(), poetBytes) - atx = newAtx(b, sigs[i], challenge, npst.NIPost, npst.NumUnits, coinbase) + atx = newAtx(challenge, npst.NIPost, npst.NumUnits, coinbase) SignAndFinalizeAtx(sigs[i], atx) vAtx, err := atx.Verify(0, 1) r.NoError(err) @@ -1491,10 +1605,16 @@ func BenchmarkGetAtxHeaderWithConcurrentProcessAtx(b *testing.B) { go func() { defer wg.Done() for i := 0; ; i++ { - challenge := newChallenge(uint64(i), types.EmptyATXID, goldenATXID, 0, nil) + challenge := types.NIPostChallenge{ + Sequence: uint64(i), + PrevATXID: types.EmptyATXID, + PublishEpoch: 0, + PositioningATX: goldenATXID, + CommitmentATX: nil, + } sig, err := signing.NewEdSigner() require.NoError(b, err) - atx := newAtx(b, sig, challenge, nil, 1, types.Address{}) + atx := newAtx(challenge, nil, 1, types.Address{}) require.NoError(b, SignAndFinalizeAtx(sig, atx)) vAtx, err := atx.Verify(0, 1) if !assert.NoError(b, err) { @@ -1523,9 +1643,6 @@ func BenchmarkGetAtxHeaderWithConcurrentProcessAtx(b *testing.B) { func TestHandler_FetchReferences(t *testing.T) { goldenATXID := types.ATXID{2, 3, 4} - sig, err := signing.NewEdSigner() - require.NoError(t, err) - posATX := types.ATXID{1, 2, 3} prevATX := types.ATXID{4, 5, 6} commitATX := types.ATXID{7, 8, 9} @@ -1535,9 +1652,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, prevATX, posATX, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevATX, + PublishEpoch: postGenesisEpoch, + PositioningATX: posATX, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()) atxHdlr.mockFetch.EXPECT(). @@ -1551,9 +1674,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, prevATX, goldenATXID, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevATX, + PublishEpoch: postGenesisEpoch, + PositioningATX: goldenATXID, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()) atxHdlr.mockFetch.EXPECT().GetAtxs(gomock.Any(), []types.ATXID{atx.PrevATXID}, gomock.Any()).Return(nil) @@ -1565,9 +1694,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, prevATX, types.EmptyATXID, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevATX, + PublishEpoch: postGenesisEpoch, + PositioningATX: types.EmptyATXID, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()) atxHdlr.mockFetch.EXPECT().GetAtxs(gomock.Any(), []types.ATXID{atx.PrevATXID}, gomock.Any()).Return(nil) @@ -1579,9 +1714,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, types.EmptyATXID, posATX, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.EmptyATXID, + PublishEpoch: postGenesisEpoch, + PositioningATX: posATX, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atx.CommitmentATX = &commitATX atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()) @@ -1595,9 +1736,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, types.EmptyATXID, posATX, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.EmptyATXID, + PublishEpoch: postGenesisEpoch, + PositioningATX: posATX, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atx.CommitmentATX = &goldenATXID atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()) @@ -1610,9 +1757,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, types.EmptyATXID, types.EmptyATXID, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.EmptyATXID, + PublishEpoch: postGenesisEpoch, + PositioningATX: types.EmptyATXID, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()) require.NoError(t, atxHdlr.FetchReferences(context.Background(), atx)) @@ -1623,9 +1776,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, prevATX, prevATX, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevATX, + PublishEpoch: postGenesisEpoch, + PositioningATX: prevATX, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()) atxHdlr.mockFetch.EXPECT().GetAtxs(gomock.Any(), []types.ATXID{atx.PrevATXID}, gomock.Any()).Return(nil) @@ -1637,9 +1796,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, types.EmptyATXID, types.EmptyATXID, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: types.EmptyATXID, + PublishEpoch: postGenesisEpoch, + PositioningATX: types.EmptyATXID, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()).Return(errors.New("pooh")) require.Error(t, atxHdlr.FetchReferences(context.Background(), atx)) @@ -1650,9 +1815,15 @@ func TestHandler_FetchReferences(t *testing.T) { atxHdlr := newTestHandler(t, goldenATXID) coinbase := types.Address{2, 4, 5} - challenge := newChallenge(1, prevATX, prevATX, types.LayerID(22).GetEpoch(), nil) + challenge := types.NIPostChallenge{ + Sequence: 1, + PrevATXID: prevATX, + PublishEpoch: postGenesisEpoch, + PositioningATX: prevATX, + CommitmentATX: nil, + } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) - atx := newAtx(t, sig, challenge, nipost.NIPost, 2, coinbase) + atx := newAtx(challenge, nipost.NIPost, 2, coinbase) atxHdlr.mockFetch.EXPECT().GetPoetProof(gomock.Any(), atx.GetPoetProofRef()) atxHdlr.mockFetch.EXPECT().GetAtxs(gomock.Any(), []types.ATXID{atx.PrevATXID}, gomock.Any()).Return(errors.New("oh")) diff --git a/activation/post_test.go b/activation/post_test.go index 6577a71118..1a67e30317 100644 --- a/activation/post_test.go +++ b/activation/post_test.go @@ -18,6 +18,7 @@ import ( "github.com/spacemeshos/go-spacemesh/log/logtest" "github.com/spacemeshos/go-spacemesh/signing" "github.com/spacemeshos/go-spacemesh/sql" + "github.com/spacemeshos/go-spacemesh/sql/atxs" ) func TestPostSetupManager(t *testing.T) { @@ -274,10 +275,21 @@ func TestPostSetupManager_Stop_WhileInProgress(t *testing.T) { func TestPostSetupManager_findCommitmentAtx_UsesLatestAtx(t *testing.T) { mgr := newTestPostManager(t) - latestAtx := addPrevAtx(t, mgr.db, 1, mgr.signer) - atx, err := mgr.findCommitmentAtx(context.Background()) + challenge := types.NIPostChallenge{ + PublishEpoch: 1, + } + atx := types.NewActivationTx(challenge, types.Address{}, nil, 2, nil) + atx.SetEffectiveNumUnits(2) + require.NoError(t, SignAndFinalizeAtx(mgr.signer, atx)) + atx.SetEffectiveNumUnits(atx.NumUnits) + atx.SetReceived(time.Now()) + vAtx, err := atx.Verify(0, 1) require.NoError(t, err) - require.Equal(t, latestAtx.ID(), atx) + require.NoError(t, atxs.Add(mgr.db, vAtx)) + + commitmentAtx, err := mgr.findCommitmentAtx(context.Background()) + require.NoError(t, err) + require.Equal(t, vAtx.ID(), commitmentAtx) } func TestPostSetupManager_findCommitmentAtx_DefaultsToGoldenAtx(t *testing.T) { @@ -312,7 +324,12 @@ func TestPostSetupManager_getCommitmentAtx_getsCommitmentAtxFromInitialAtx(t *te commitmentAtx := types.RandomATXID() atx := types.NewActivationTx(types.NIPostChallenge{}, types.Address{}, nil, 1, nil) atx.CommitmentATX = &commitmentAtx - addAtx(t, mgr.cdb, mgr.signer, atx) + require.NoError(t, SignAndFinalizeAtx(mgr.signer, atx)) + atx.SetEffectiveNumUnits(atx.NumUnits) + atx.SetReceived(time.Now()) + vAtx, err := atx.Verify(0, 1) + require.NoError(t, err) + require.NoError(t, atxs.Add(mgr.cdb, vAtx)) atxid, err := mgr.commitmentAtx(context.Background(), mgr.opts.DataDir) require.NoError(t, err) diff --git a/activation/validation_test.go b/activation/validation_test.go index af42d52c78..4eb5ffbc55 100644 --- a/activation/validation_test.go +++ b/activation/validation_test.go @@ -96,7 +96,14 @@ func Test_Validation_InitialNIPostChallenge(t *testing.T) { posAtxId := types.ATXID{1, 2, 3} commitmentAtxId := types.ATXID{5, 6, 7} - challenge := newChallenge(0, types.EmptyATXID, posAtxId, 2, &commitmentAtxId) + var PublishEpoch types.EpochID = 2 + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: PublishEpoch, + PositioningATX: posAtxId, + CommitmentATX: &commitmentAtxId, + } challenge.InitialPost = &types.Post{} atxProvider := NewMockatxProvider(ctrl) @@ -115,7 +122,14 @@ func Test_Validation_InitialNIPostChallenge(t *testing.T) { posAtxId := types.ATXID{1, 2, 3} - challenge := newChallenge(0, types.EmptyATXID, posAtxId, types.LayerID(2).GetEpoch(), &goldenATXID) + var PublishEpoch types.EpochID = types.LayerID(2).GetEpoch() + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: PublishEpoch, + PositioningATX: posAtxId, + CommitmentATX: &goldenATXID, + } challenge.InitialPost = &types.Post{} atxProvider := NewMockatxProvider(ctrl) @@ -130,7 +144,14 @@ func Test_Validation_InitialNIPostChallenge(t *testing.T) { posAtxId := types.ATXID{1, 2, 3} commitmentAtxId := types.ATXID{5, 6, 7} - challenge := newChallenge(0, types.EmptyATXID, posAtxId, 1, &commitmentAtxId) + var PublishEpoch types.EpochID = 1 + challenge := types.NIPostChallenge{ + Sequence: 0, + PrevATXID: types.EmptyATXID, + PublishEpoch: PublishEpoch, + PositioningATX: posAtxId, + CommitmentATX: &commitmentAtxId, + } challenge.InitialPost = &types.Post{} atxProvider := NewMockatxProvider(ctrl) @@ -166,7 +187,14 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - challenge := newChallenge(10, prevAtxId, posAtxId, 2, nil) + var PublishEpoch types.EpochID = 2 + challenge := types.NIPostChallenge{ + Sequence: 10, + PrevATXID: prevAtxId, + PublishEpoch: PublishEpoch, + PositioningATX: posAtxId, + CommitmentATX: nil, + } atxProvider := NewMockatxProvider(ctrl) atxProvider.EXPECT().GetAtxHeader(prevAtxId).Return(&types.ActivationTxHeader{ @@ -189,7 +217,14 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - challenge := newChallenge(10, prevAtxId, posAtxId, types.LayerID(1012).GetEpoch(), nil) + var PublishEpoch types.EpochID = types.LayerID(1012).GetEpoch() + challenge := types.NIPostChallenge{ + Sequence: 10, + PrevATXID: prevAtxId, + PublishEpoch: PublishEpoch, + PositioningATX: posAtxId, + CommitmentATX: nil, + } atxProvider := NewMockatxProvider(ctrl) atxProvider.EXPECT().GetAtxHeader(prevAtxId).Return(nil, errors.New("not found")) @@ -208,7 +243,14 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - challenge := newChallenge(10, prevAtxId, posAtxId, types.LayerID(1012).GetEpoch(), nil) + var PublishEpoch types.EpochID = types.LayerID(1012).GetEpoch() + challenge := types.NIPostChallenge{ + Sequence: 10, + PrevATXID: prevAtxId, + PublishEpoch: PublishEpoch, + PositioningATX: posAtxId, + CommitmentATX: nil, + } atxProvider := NewMockatxProvider(ctrl) atxProvider.EXPECT().GetAtxHeader(prevAtxId).Return(&types.ActivationTxHeader{ @@ -231,7 +273,14 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - challenge := newChallenge(10, prevAtxId, posAtxId, 2, nil) + var PublishEpoch types.EpochID = 2 + challenge := types.NIPostChallenge{ + Sequence: 10, + PrevATXID: prevAtxId, + PublishEpoch: PublishEpoch, + PositioningATX: posAtxId, + CommitmentATX: nil, + } atxProvider := NewMockatxProvider(ctrl) atxProvider.EXPECT().GetAtxHeader(prevAtxId).Return(&types.ActivationTxHeader{ @@ -254,7 +303,14 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - challenge := newChallenge(10, prevAtxId, posAtxId, 2, nil) + var PublishEpoch types.EpochID = 2 + challenge := types.NIPostChallenge{ + Sequence: 10, + PrevATXID: prevAtxId, + PublishEpoch: PublishEpoch, + PositioningATX: posAtxId, + CommitmentATX: nil, + } atxProvider := NewMockatxProvider(ctrl) atxProvider.EXPECT().GetAtxHeader(prevAtxId).Return(&types.ActivationTxHeader{ From 4b089ec75598347b6c4ca20bd25e88b7c7b5250c Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:42:37 +0000 Subject: [PATCH 07/26] Allow tests with multiple identities --- activation/activation_multi_test.go | 52 +++++ activation/activation_test.go | 321 ++++++++++++++++++---------- 2 files changed, 259 insertions(+), 114 deletions(-) create mode 100644 activation/activation_multi_test.go diff --git a/activation/activation_multi_test.go b/activation/activation_multi_test.go new file mode 100644 index 0000000000..25ab738951 --- /dev/null +++ b/activation/activation_multi_test.go @@ -0,0 +1,52 @@ +package activation + +import ( + "context" + "testing" + + "github.com/spacemeshos/post/shared" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/signing" +) + +func Test_Builder_Multi_StartSmeshingCoinbase(t *testing.T) { + tab := newTestBuilder(t) + coinbase := types.Address{1, 1, 1} + + smeshers := make(map[types.NodeID]*signing.EdSigner, 10) + for i := 0; i < 10; i++ { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + tab.Register(sig) + + smeshers[sig.NodeID()] = sig + } + + for _, sig := range smeshers { + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { + <-ctx.Done() + return nil, nil, ctx.Err() + }) + } + tab.mValidator.EXPECT(). + Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + AnyTimes(). + Return(nil) + tab.mclock.EXPECT().CurrentLayer().Return(types.LayerID(0)).AnyTimes() + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).AnyTimes() + require.NoError(t, tab.StartSmeshing(coinbase)) + require.Equal(t, coinbase, tab.Coinbase()) + + // calling StartSmeshing more than once before calling StopSmeshing is an error + require.ErrorContains(t, tab.StartSmeshing(coinbase), "already started") + + for _, sig := range smeshers { + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) + } + require.NoError(t, tab.StopSmeshing(true)) +} diff --git a/activation/activation_test.go b/activation/activation_test.go index cc443d1f32..b6a7f407cf 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -58,7 +58,7 @@ func newAtx( } func newActivationTx( - t testing.TB, + tb testing.TB, sig *signing.EdSigner, sequence uint64, prevATX types.ATXID, @@ -85,9 +85,9 @@ func newActivationTx( atx.SetEffectiveNumUnits(numUnits) atx.SetReceived(time.Now()) - require.NoError(t, SignAndFinalizeAtx(sig, atx)) + require.NoError(tb, SignAndFinalizeAtx(sig, atx)) vAtx, err := atx.Verify(startTick, numTicks) - require.NoError(t, err) + require.NoError(tb, err) return vAtx } @@ -95,7 +95,6 @@ type testAtxBuilder struct { *Builder cdb *datastore.CachedDB localDb *localsql.Database - sig *signing.EdSigner goldenATXID types.ATXID mpub *mocks.MockPublisher @@ -108,14 +107,13 @@ type testAtxBuilder struct { func newTestBuilder(tb testing.TB, opts ...BuilderOption) *testAtxBuilder { lg := logtest.New(tb) - edSigner, err := signing.NewEdSigner() - require.NoError(tb, err) + ctrl := gomock.NewController(tb) tab := &testAtxBuilder{ cdb: datastore.NewCachedDB(sql.InMemory(), lg), localDb: localsql.InMemory(), - sig: edSigner, goldenATXID: types.ATXID(types.HexToHash32("77777")), + mpub: mocks.NewMockPublisher(ctrl), mnipost: NewMocknipostBuilder(ctrl), mpostClient: NewMockPostClient(ctrl), @@ -144,19 +142,23 @@ func newTestBuilder(tb testing.TB, opts ...BuilderOption) *testAtxBuilder { lg.Zap(), opts..., ) - b.Register(tab.sig) tab.Builder = b return tab } func publishAtx( - t *testing.T, + tb testing.TB, tab *testAtxBuilder, + nodeID types.NodeID, posEpoch types.EpochID, currLayer *types.LayerID, // pointer to keep current layer consistent across calls buildNIPostLayerDuration uint32, ) (*types.ActivationTx, error) { - t.Helper() + tb.Helper() + + if _, ok := tab.signers[nodeID]; !ok { + return nil, fmt.Errorf("node %v not registered", nodeID) + } publishEpoch := posEpoch + 1 tab.mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( @@ -168,7 +170,7 @@ func publishAtx( nonce := types.VRFPostIndex(123) commitmentATX := types.RandomATXID() tab.mpostClient.EXPECT().Info(gomock.Any()).Return(&types.PostInfo{ - NodeID: tab.sig.NodeID(), + NodeID: nodeID, CommitmentATX: commitmentATX, Nonce: &nonce, @@ -178,7 +180,7 @@ func publishAtx( tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, _ *signing.EdSigner, challenge *types.NIPostChallenge) (*nipost.NIPostState, error) { *currLayer = currLayer.Add(buildNIPostLayerDuration) - return newNIPostWithChallenge(t, challenge.Hash(), []byte("66666")), nil + return newNIPostWithChallenge(tb, challenge.Hash(), []byte("66666")), nil }) ch := make(chan struct{}) close(ch) @@ -194,30 +196,34 @@ func publishAtx( tab.mpub.EXPECT().Publish(gomock.Any(), pubsub.AtxProtocol, gomock.Any()).DoAndReturn( func(_ context.Context, _ string, got []byte) error { var gotAtx types.ActivationTx - require.NoError(t, codec.Decode(got, &gotAtx)) + require.NoError(tb, codec.Decode(got, &gotAtx)) gotAtx.SetReceived(time.Now().Local()) built = &gotAtx - require.NoError(t, built.Initialize()) + require.NoError(tb, built.Initialize()) built.SetEffectiveNumUnits(gotAtx.NumUnits) vatx, err := built.Verify(0, 1) - require.NoError(t, err) - require.NoError(t, atxs.Add(tab.cdb, vatx)) + require.NoError(tb, err) + require.NoError(tb, atxs.Add(tab.cdb, vatx)) return nil }) - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(nodeID).Return(nil) // create and publish ATX - err := tab.PublishActivationTx(context.Background(), tab.sig) + err := tab.PublishActivationTx(context.Background(), tab.signers[nodeID]) return built, err } // ========== Tests ========== -func TestBuilder_StartSmeshingCoinbase(t *testing.T) { +func Test_Builder_StartSmeshingCoinbase(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t) + tab.Register(sig) coinbase := types.Address{1, 1, 1} - tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() return nil, nil, ctx.Err() @@ -234,14 +240,19 @@ func TestBuilder_StartSmeshingCoinbase(t *testing.T) { // calling StartSmeshing more than once before calling StopSmeshing is an error require.ErrorContains(t, tab.StartSmeshing(coinbase), "already started") - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) require.NoError(t, tab.StopSmeshing(true)) } func TestBuilder_RestartSmeshing(t *testing.T) { getBuilder := func(t *testing.T) *Builder { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t) - tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).AnyTimes().DoAndReturn( + tab.Register(sig) + + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).AnyTimes().DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() return nil, nil, ctx.Err() @@ -284,7 +295,11 @@ func TestBuilder_RestartSmeshing(t *testing.T) { } func TestBuilder_StopSmeshing_Delete(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t) + tab.Register(sig) currLayer := (postGenesisEpoch + 1).FirstLayer() tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).AnyTimes() @@ -301,7 +316,7 @@ func TestBuilder_StopSmeshing_Delete(t *testing.T) { return nil, ctx.Err() }). AnyTimes() - tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() return nil, nil, ctx.Err() @@ -312,29 +327,28 @@ func TestBuilder_StopSmeshing_Delete(t *testing.T) { PublishEpoch: postGenesisEpoch + 2, CommitmentATX: &types.ATXID{1, 2, 3}, } - err := nipost.AddChallenge(tab.localDb, tab.sig.NodeID(), refChallenge) - require.NoError(t, err) + require.NoError(t, nipost.AddChallenge(tab.localDb, sig.NodeID(), refChallenge)) require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(false)) - challenge, err := nipost.Challenge(tab.localDb, tab.sig.NodeID()) + challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) require.NoError(t, err) require.Equal(t, refChallenge, challenge) // challenge still present - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) - challenge, err = nipost.Challenge(tab.localDb, tab.sig.NodeID()) + challenge, err = nipost.Challenge(tab.localDb, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) require.Nil(t, challenge) // challenge deleted - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) // no-op - challenge, err = nipost.Challenge(tab.localDb, tab.sig.NodeID()) + challenge, err = nipost.Challenge(tab.localDb, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) require.Nil(t, challenge) // challenge still deleted } @@ -345,7 +359,12 @@ func TestBuilder_StopSmeshing_failsWhenNotStarted(t *testing.T) { } func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration})) + tab.Register(sig) + posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() ch := types.NIPostChallenge{ @@ -357,27 +376,27 @@ func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(tab.sig, prevAtx) + SignAndFinalizeAtx(sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) // create and publish ATX tab.mclock.EXPECT().CurrentLayer().Return(currLayer).Times(4) - atx1, err := publishAtx(t, tab, posEpoch, &currLayer, layersPerEpoch) + atx1, err := publishAtx(t, tab, sig.NodeID(), posEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) require.NotNil(t, atx1) // create and publish another ATX currLayer = (posEpoch + 1).FirstLayer() tab.mclock.EXPECT().CurrentLayer().Return(currLayer).Times(4) - atx2, err := publishAtx(t, tab, atx1.PublishEpoch, &currLayer, layersPerEpoch) + atx2, err := publishAtx(t, tab, sig.NodeID(), atx1.PublishEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) require.NotEqual(t, atx1, atx2) require.Equal(t, atx1.TargetEpoch()+1, atx2.TargetEpoch()) // state is cleaned up - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } @@ -385,7 +404,12 @@ func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { // failing with ErrATXChallengeExpired. func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { // Arrange + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + tab.Register(sig) + // current layer is too late to be able to build a nipost on time currLayer := (postGenesisEpoch + 1).FirstLayer() ch := types.NIPostChallenge{ @@ -397,7 +421,7 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(tab.sig, prevAtx) + SignAndFinalizeAtx(sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -411,7 +435,7 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { }).AnyTimes() tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, ErrATXChallengeExpired) - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -425,19 +449,24 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { // Act & Verify var eg errgroup.Group eg.Go(func() error { - tab.run(ctx, tab.sig) + tab.run(ctx, sig) return nil }) require.NoError(t, eg.Wait()) // state is cleaned up - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + tab.Register(sig) + posEpoch := postGenesisEpoch currLayer := postGenesisEpoch.FirstLayer() ch := types.NIPostChallenge{ @@ -449,7 +478,7 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(tab.sig, prevAtx) + SignAndFinalizeAtx(sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -465,7 +494,7 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { nonce := types.VRFPostIndex(123) commitmentATX := types.RandomATXID() tab.mpostClient.EXPECT().Info(gomock.Any()).Return(&types.PostInfo{ - NodeID: tab.sig.NodeID(), + NodeID: sig.NodeID(), CommitmentATX: commitmentATX, Nonce: &nonce, @@ -501,7 +530,7 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { ) // after successful publish, state is cleaned up - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) tab.mpub.EXPECT().Publish(gomock.Any(), pubsub.AtxProtocol, gomock.Any()).DoAndReturn( // second publish succeeds @@ -515,18 +544,23 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { }, ) // create and publish ATX - require.NoError(t, tab.PublishActivationTx(context.Background(), tab.sig)) + require.NoError(t, tab.PublishActivationTx(context.Background(), sig)) // state is cleaned up - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + poetCfg := PoetConfig{ PhaseShift: layerDuration * 4, } tab := newTestBuilder(t, WithPoetConfig(poetCfg)) + tab.Register(sig) + posEpoch := postGenesisEpoch currLayer := (postGenesisEpoch + 1).FirstLayer().Add(5) // late for poet round start challenge := types.NIPostChallenge{ @@ -538,7 +572,7 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(challenge, nipostData.NIPost, posEpoch.Uint32(), types.Address{}) - SignAndFinalizeAtx(tab.sig, prevAtx) + SignAndFinalizeAtx(sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -554,7 +588,7 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi nonce := types.VRFPostIndex(123) commitmentATX := types.RandomATXID() tab.mpostClient.EXPECT().Info(gomock.Any()).Return(&types.PostInfo{ - NodeID: tab.sig.NodeID(), + NodeID: sig.NodeID(), CommitmentATX: commitmentATX, Nonce: &nonce, @@ -585,9 +619,9 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi PositioningATX: vPrevAtx.ID(), } - require.NoError(t, nipost.AddChallenge(tab.localDb, tab.sig.NodeID(), ch)) + require.NoError(t, nipost.AddChallenge(tab.localDb, sig.NodeID(), ch)) - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) tab.mpub.EXPECT().Publish(gomock.Any(), pubsub.AtxProtocol, gomock.Any()).DoAndReturn( // publish succeeds @@ -602,15 +636,20 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi ) // create and publish ATX - require.NoError(t, tab.PublishActivationTx(context.Background(), tab.sig)) + require.NoError(t, tab.PublishActivationTx(context.Background(), sig)) // state is cleaned up - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + tab.Register(sig) + posEpoch := types.EpochID(2) currLayer := posEpoch.FirstLayer() ch := types.NIPostChallenge{ @@ -622,7 +661,7 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(tab.sig, prevAtx) + SignAndFinalizeAtx(sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -671,12 +710,12 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi }, ) // create and publish ATX - err = tab.PublishActivationTx(ctx, tab.sig) + err = tab.PublishActivationTx(ctx, sig) require.ErrorIs(t, err, context.Canceled) // publish returning an error will just cause a retry if not canceled require.NotNil(t, built) // state is preserved for a retry - challenge, err := nipost.Challenge(tab.localDB, tab.sig.NodeID()) + challenge, err := nipost.Challenge(tab.localDB, sig.NodeID()) require.NoError(t, err) require.Equal(t, built.NIPostChallenge, *challenge) @@ -693,25 +732,30 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi CommitmentATX: nil, } posAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(tab.sig, posAtx) + SignAndFinalizeAtx(sig, posAtx) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPosAtx)) tab.mclock.EXPECT().CurrentLayer().DoAndReturn(func() types.LayerID { return currLayer }).AnyTimes() - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) - built2, err := publishAtx(t, tab, posEpoch, &currLayer, layersPerEpoch) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) + built2, err := publishAtx(t, tab, sig.NodeID(), posEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) require.NotNil(t, built2) require.NotEqual(t, built.NIPostChallenge, built2.NIPostChallenge) require.Equal(t, posEpoch+2, built2.TargetEpoch()) // state is cleaned up after successful publish - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } func TestBuilder_PublishActivationTx_NoPrevATX(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + tab.Register(sig) + posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() challenge := types.NIPostChallenge{ @@ -733,23 +777,28 @@ func TestBuilder_PublishActivationTx_NoPrevATX(t *testing.T) { // generate and store initial post in state require.NoError(t, nipost.AddInitialPost( tab.localDb, - tab.sig.NodeID(), + sig.NodeID(), nipost.Post{Indices: make([]byte, 10)}, )) // create and publish ATX tab.mclock.EXPECT().CurrentLayer().Return(currLayer).AnyTimes() - atx, err := publishAtx(t, tab, posEpoch, &currLayer, layersPerEpoch) + atx, err := publishAtx(t, tab, sig.NodeID(), posEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) require.NotNil(t, atx) // state is cleaned up - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserved(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + tab.Register(sig) + posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() challenge := types.NIPostChallenge{ @@ -773,11 +822,7 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve Indices: make([]byte, 10), CommitmentATX: types.RandomATXID(), } - require.NoError(t, nipost.AddInitialPost( - tab.localDb, - tab.sig.NodeID(), - refPost, - )) + require.NoError(t, nipost.AddInitialPost(tab.localDb, sig.NodeID(), refPost)) // create and publish ATX tab.mclock.EXPECT().CurrentLayer().Return(currLayer).AnyTimes() @@ -789,7 +834,7 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve }).AnyTimes() tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, ErrATXChallengeExpired) - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) ch := make(chan struct{}) tab.mclock.EXPECT().AwaitLayer(currLayer.Add(1)).Do(func(got types.LayerID) <-chan struct{} { @@ -800,7 +845,7 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve ctx, cancel := context.WithCancel(context.Background()) var eg errgroup.Group eg.Go(func() error { - tab.run(ctx, tab.sig) + tab.run(ctx, sig) return nil }) t.Cleanup(func() { @@ -815,13 +860,13 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve } // initial post is preserved - post, err := nipost.InitialPost(tab.localDB, tab.sig.NodeID()) + post, err := nipost.InitialPost(tab.localDB, sig.NodeID()) require.NoError(t, err) require.NotNil(t, post) require.Equal(t, refPost, *post) // state is cleaned up - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } @@ -829,7 +874,12 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { r := require.New(t) // Arrange + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + tab.Register(sig) + otherSigner, err := signing.NewEdSigner() r.NoError(err) @@ -867,7 +917,7 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { challenge.InitialPost = initialPost prevAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) prevAtx.InitialPost = initialPost - SignAndFinalizeAtx(tab.sig, prevAtx) + SignAndFinalizeAtx(sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) r.NoError(err) r.NoError(atxs.Add(tab.cdb, vPrevAtx)) @@ -894,7 +944,7 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { nonce := types.VRFPostIndex(123) commitmentATX := types.RandomATXID() tab.mpostClient.EXPECT().Info(gomock.Any()).Return(&types.PostInfo{ - NodeID: tab.sig.NodeID(), + NodeID: sig.NodeID(), CommitmentATX: commitmentATX, Nonce: &nonce, @@ -918,7 +968,7 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { atx.SetEffectiveNumUnits(atx.NumUnits) vAtx, err := atx.Verify(0, 1) r.NoError(err) - r.Equal(tab.sig.NodeID(), vAtx.SmesherID) + r.Equal(sig.NodeID(), vAtx.SmesherID) r.NoError(atxs.Add(tab.cdb, vAtx)) @@ -935,12 +985,12 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { return nil }) - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) - r.NoError(tab.PublishActivationTx(context.Background(), tab.sig)) + r.NoError(tab.PublishActivationTx(context.Background(), sig)) // state is cleaned up - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } @@ -948,7 +998,12 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { r := require.New(t) // Arrange + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + tab.Register(sig) + otherSigner, err := signing.NewEdSigner() r.NoError(err) @@ -991,7 +1046,7 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { nonce := types.VRFPostIndex(123) commitmentATX := types.RandomATXID() tab.mpostClient.EXPECT().Info(gomock.Any()).Return(&types.PostInfo{ - NodeID: tab.sig.NodeID(), + NodeID: sig.NodeID(), CommitmentATX: commitmentATX, Nonce: &nonce, @@ -1015,7 +1070,7 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { atx.SetEffectiveNumUnits(atx.NumUnits) vAtx, err := atx.Verify(0, 1) r.NoError(err) - r.Equal(tab.sig.NodeID(), vAtx.SmesherID) + r.Equal(sig.NodeID(), vAtx.SmesherID) r.NoError(atxs.Add(tab.cdb, vAtx)) @@ -1030,23 +1085,24 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { return nil }) - require.NoError(t, nipost.AddInitialPost( - tab.localDb, - tab.sig.NodeID(), - nipost.Post{Indices: make([]byte, 10)}, - )) + require.NoError(t, nipost.AddInitialPost(tab.localDb, sig.NodeID(), nipost.Post{Indices: make([]byte, 10)})) - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) - r.NoError(tab.PublishActivationTx(context.Background(), tab.sig)) + r.NoError(tab.PublishActivationTx(context.Background(), sig)) // state is cleaned up - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + tab.Register(sig) + posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() ch := types.NIPostChallenge{ @@ -1058,7 +1114,7 @@ func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) posAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(tab.sig, posAtx) + SignAndFinalizeAtx(sig, posAtx) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPosAtx)) @@ -1072,10 +1128,10 @@ func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { }).AnyTimes() nipostErr := fmt.Errorf("NIPost builder error") tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nipostErr) - require.ErrorIs(t, tab.PublishActivationTx(context.Background(), tab.sig), nipostErr) + require.ErrorIs(t, tab.PublishActivationTx(context.Background(), sig), nipostErr) // state is preserved - challenge, err := nipost.Challenge(tab.localDB, tab.sig.NodeID()) + challenge, err := nipost.Challenge(tab.localDB, sig.NodeID()) require.NoError(t, err) require.NotNil(t, challenge) } @@ -1131,7 +1187,12 @@ func TestBuilder_PublishActivationTx_Serialize(t *testing.T) { } func TestBuilder_SignAtx(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t) + tab.Register(sig) + prevAtx := types.ATXID(types.HexToHash32("0x111")) challenge := types.NIPostChallenge{ Sequence: 1, @@ -1142,11 +1203,11 @@ func TestBuilder_SignAtx(t *testing.T) { } nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) atx := newAtx(challenge, nipost.NIPost, 100, types.Address{}) - require.NoError(t, SignAndFinalizeAtx(tab.sig, atx)) + require.NoError(t, SignAndFinalizeAtx(sig, atx)) - ok := signing.NewEdVerifier().Verify(signing.ATX, tab.sig.NodeID(), atx.SignedBytes(), atx.Signature) + ok := signing.NewEdVerifier().Verify(signing.ATX, sig.NodeID(), atx.SignedBytes(), atx.Signature) require.True(t, ok) - require.Equal(t, tab.sig.NodeID(), atx.SmesherID) + require.Equal(t, sig.NodeID(), atx.SmesherID) } func TestBuilder_RetryPublishActivationTx(t *testing.T) { @@ -1161,6 +1222,9 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { }, events.WithBuffer(100)) require.NoError(t, err) + sig, err := signing.NewEdSigner() + require.NoError(t, err) + retryInterval := 50 * time.Microsecond tab := newTestBuilder( t, @@ -1178,7 +1242,7 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { poetBytes := []byte("66666") nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetBytes) prevAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(tab.sig, prevAtx) + SignAndFinalizeAtx(sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -1225,12 +1289,12 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { }, ) - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) nonce := types.VRFPostIndex(123) commitmentATX := types.RandomATXID() tab.mpostClient.EXPECT().Info(gomock.Any()).Return(&types.PostInfo{ - NodeID: tab.sig.NodeID(), + NodeID: sig.NodeID(), CommitmentATX: commitmentATX, Nonce: &nonce, @@ -1255,7 +1319,7 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { defer cancel() var eg errgroup.Group eg.Go(func() error { - tab.run(ctx, tab.sig) + tab.run(ctx, sig) return nil }) t.Cleanup(func() { assert.NoError(t, eg.Wait()) }) @@ -1276,16 +1340,21 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { } // state is cleaned up - _, err = nipost.InitialPost(tab.localDB, tab.sig.NodeID()) + _, err = nipost.InitialPost(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) - _, err = nipost.Challenge(tab.localDB, tab.sig.NodeID()) + _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) } func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).Return( + tab.Register(sig) + + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).Return( &types.Post{Indices: make([]byte, 10)}, &types.PostInfo{ CommitmentATX: types.RandomATXID(), @@ -1297,7 +1366,7 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). AnyTimes(). Return(nil) - require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) + require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) posEpoch := postGenesisEpoch + 1 challenge := types.NIPostChallenge{ @@ -1310,18 +1379,18 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { poetByte := []byte("66666") nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetByte) prevAtx := newAtx(challenge, nipost.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(tab.sig, prevAtx) + SignAndFinalizeAtx(sig, prevAtx) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) currLayer := posEpoch.FirstLayer().Add(1) tab.mclock.EXPECT().CurrentLayer().Return(currLayer).AnyTimes() - atx, err := publishAtx(t, tab, posEpoch, &currLayer, layersPerEpoch) + atx, err := publishAtx(t, tab, sig.NodeID(), posEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) require.NotNil(t, atx) - require.Equal(t, tab.sig.NodeID(), atx.SmesherID) + require.Equal(t, sig.NodeID(), atx.SmesherID) require.Equal(t, vPrevAtx.Sequence+1, atx.Sequence) require.Equal(t, vPrevAtx.ID(), atx.PrevATXID) require.Nil(t, atx.InitialPost) @@ -1331,12 +1400,17 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { require.Equal(t, types.BytesToHash(poetByte), atx.GetPoetProofRef()) // postClient.Proof() should not be called again - require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) + require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) } func TestBuilder_InitialPostIsPersisted(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.mnipost.EXPECT().Proof(gomock.Any(), tab.sig.NodeID(), shared.ZeroChallenge).Return( + tab.Register(sig) + + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).Return( &types.Post{Indices: make([]byte, 10)}, &types.PostInfo{ CommitmentATX: types.RandomATXID(), @@ -1348,10 +1422,10 @@ func TestBuilder_InitialPostIsPersisted(t *testing.T) { Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). AnyTimes(). Return(nil) - require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) + require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) // postClient.Proof() should not be called again - require.NoError(t, tab.buildInitialPost(context.Background(), tab.sig.NodeID())) + require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) } func TestWaitPositioningAtx(t *testing.T) { @@ -1368,10 +1442,15 @@ func TestWaitPositioningAtx(t *testing.T) { } { tc := tc t.Run(tc.desc, func(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{ PhaseShift: tc.shift, GracePeriod: tc.grace, })) + tab.Register(sig) + tab.mclock.EXPECT().CurrentLayer().Return(types.LayerID(0)).AnyTimes() tab.mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn(func(lid types.LayerID) time.Time { // layer duration is 10ms to speed up test @@ -1380,7 +1459,7 @@ func TestWaitPositioningAtx(t *testing.T) { // everything else are stubs that are irrelevant for the test tab.mpostClient.EXPECT().Info(gomock.Any()).Return(&types.PostInfo{}, nil).AnyTimes() - tab.mnipost.EXPECT().ResetState(tab.sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(&nipost.NIPostState{}, nil) closed := make(chan struct{}) close(closed) @@ -1396,12 +1475,11 @@ func TestWaitPositioningAtx(t *testing.T) { require.NoError(t, nipost.AddInitialPost( tab.localDb, - tab.sig.NodeID(), + sig.NodeID(), nipost.Post{Indices: make([]byte, 10)}, )) - err := tab.PublishActivationTx(context.Background(), tab.sig) - require.NoError(t, err) + require.NoError(t, tab.PublishActivationTx(context.Background(), sig)) }) } } @@ -1409,14 +1487,24 @@ func TestWaitPositioningAtx(t *testing.T) { func TestRegossip(t *testing.T) { layer := types.LayerID(10) t.Run("not found", func(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t) + tab.Register(sig) + tab.mclock.EXPECT().CurrentLayer().Return(layer) - require.NoError(t, tab.Regossip(context.Background(), tab.sig.NodeID())) + require.NoError(t, tab.Regossip(context.Background(), sig.NodeID())) }) t.Run("success", func(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t) + tab.Register(sig) + atx := newActivationTx(t, - tab.sig, 0, types.EmptyATXID, types.EmptyATXID, nil, + sig, 0, types.EmptyATXID, types.EmptyATXID, nil, layer.GetEpoch(), 0, 1, types.Address{}, 1, &types.NIPost{}) require.NoError(t, atxs.Add(tab.cdb.Database, atx)) blob, err := atxs.GetBlob(tab.cdb.Database, atx.ID().Bytes()) @@ -1425,14 +1513,19 @@ func TestRegossip(t *testing.T) { ctx := context.Background() tab.mpub.EXPECT().Publish(ctx, pubsub.AtxProtocol, blob) - require.NoError(t, tab.Regossip(ctx, tab.sig.NodeID())) + require.NoError(t, tab.Regossip(ctx, sig.NodeID())) }) t.Run("checkpointed", func(t *testing.T) { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + tab := newTestBuilder(t) + tab.Register(sig) + require.NoError(t, atxs.AddCheckpointed(tab.cdb.Database, - &atxs.CheckpointAtx{ID: types.ATXID{1}, Epoch: layer.GetEpoch(), SmesherID: tab.sig.NodeID()})) + &atxs.CheckpointAtx{ID: types.ATXID{1}, Epoch: layer.GetEpoch(), SmesherID: sig.NodeID()})) tab.mclock.EXPECT().CurrentLayer().Return(layer) - require.NoError(t, tab.Regossip(context.Background(), tab.sig.NodeID())) + require.NoError(t, tab.Regossip(context.Background(), sig.NodeID())) }) } From eb299b0efea4d6da276e7651a0aa312a0bbde74d Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:31:37 +0000 Subject: [PATCH 08/26] Add tests for multi-smesher setup --- activation/activation_multi_test.go | 243 +++++++++++++++++++++++++++- activation/activation_test.go | 81 ++-------- 2 files changed, 259 insertions(+), 65 deletions(-) diff --git a/activation/activation_multi_test.go b/activation/activation_multi_test.go index 25ab738951..e6227e928e 100644 --- a/activation/activation_multi_test.go +++ b/activation/activation_multi_test.go @@ -3,13 +3,19 @@ package activation import ( "context" "testing" + "time" "github.com/spacemeshos/post/shared" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/p2p/pubsub" "github.com/spacemeshos/go-spacemesh/signing" + "github.com/spacemeshos/go-spacemesh/sql" + "github.com/spacemeshos/go-spacemesh/sql/atxs" + "github.com/spacemeshos/go-spacemesh/sql/localsql/nipost" ) func Test_Builder_Multi_StartSmeshingCoinbase(t *testing.T) { @@ -22,7 +28,6 @@ func Test_Builder_Multi_StartSmeshingCoinbase(t *testing.T) { require.NoError(t, err) tab.Register(sig) - smeshers[sig.NodeID()] = sig } @@ -50,3 +55,239 @@ func Test_Builder_Multi_StartSmeshingCoinbase(t *testing.T) { } require.NoError(t, tab.StopSmeshing(true)) } + +func Test_Builder_Multi_RestartSmeshing(t *testing.T) { + getBuilder := func(t *testing.T) *Builder { + tab := newTestBuilder(t) + + smeshers := make(map[types.NodeID]*signing.EdSigner, 10) + for i := 0; i < 10; i++ { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + tab.Register(sig) + smeshers[sig.NodeID()] = sig + + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).AnyTimes().DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { + <-ctx.Done() + return nil, nil, ctx.Err() + }) + } + + ch := make(chan struct{}) + close(ch) + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(ch).AnyTimes() + tab.mclock.EXPECT().CurrentLayer().Return(types.LayerID(0)).AnyTimes() + return tab.Builder + } + + t.Run("Single threaded", func(t *testing.T) { + builder := getBuilder(t) + for i := 0; i < 50; i++ { + require.NoError(t, builder.StartSmeshing(types.Address{})) + require.True(t, builder.Smeshing()) + require.NoError(t, builder.StopSmeshing(false)) + require.False(t, builder.Smeshing()) + } + }) + + t.Run("Multi threaded", func(t *testing.T) { + // Meant to be run with -race to detect races. + // It cannot check `builder.Smeshing()` as Start/Stop is happening from many goroutines simultaneously. + // Both Start and Stop can fail as it is not known if builder is smeshing or not. + builder := getBuilder(t) + var eg errgroup.Group + for worker := 0; worker < 10; worker += 1 { + eg.Go(func() error { + for i := 0; i < 50; i++ { + builder.StartSmeshing(types.Address{}) + builder.StopSmeshing(false) + } + return nil + }) + } + require.NoError(t, eg.Wait()) + }) +} + +func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { + tab := newTestBuilder(t) + + atx := types.RandomATXID() + refChallenge := &types.NIPostChallenge{ + PublishEpoch: postGenesisEpoch + 2, + CommitmentATX: &atx, + } + + currLayer := (postGenesisEpoch + 1).FirstLayer() + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).AnyTimes() + tab.mclock.EXPECT().CurrentLayer().Return(currLayer).AnyTimes() + tab.mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( + func(got types.LayerID) time.Time { + // time.Now() ~= currentLayer + genesis := time.Now().Add(-time.Duration(currLayer) * layerDuration) + return genesis.Add(layerDuration * time.Duration(got)) + }).AnyTimes() + + smeshers := make(map[types.NodeID]*signing.EdSigner, 10) + for i := 0; i < 10; i++ { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + tab.Register(sig) + smeshers[sig.NodeID()] = sig + + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { + <-ctx.Done() + return nil, nil, ctx.Err() + }) + + // add challenge to DB + require.NoError(t, nipost.AddChallenge(tab.localDb, sig.NodeID(), refChallenge)) + } + + require.NoError(t, tab.StartSmeshing(types.Address{})) + require.NoError(t, tab.StopSmeshing(false)) + + for _, sig := range smeshers { + challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) + require.NoError(t, err) + require.Equal(t, refChallenge, challenge) // challenge still present + + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) + + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { + <-ctx.Done() + return nil, nil, ctx.Err() + }) + } + + require.NoError(t, tab.StartSmeshing(types.Address{})) + require.NoError(t, tab.StopSmeshing(true)) + + for _, sig := range smeshers { + challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) + require.ErrorIs(t, err, sql.ErrNotFound) + require.Nil(t, challenge) // challenge deleted + + tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) + + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { + <-ctx.Done() + return nil, nil, ctx.Err() + }) + } + + require.NoError(t, tab.StartSmeshing(types.Address{})) + require.NoError(t, tab.StopSmeshing(true)) // no-op + + for _, sig := range smeshers { + challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) + require.ErrorIs(t, err, sql.ErrNotFound) + require.Nil(t, challenge) // challenge still deleted + } +} + +func TestRegossip(t *testing.T) { + layer := types.LayerID(10) + + smeshers := make(map[types.NodeID]*signing.EdSigner, 10) + for i := 0; i < 10; i++ { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + smeshers[sig.NodeID()] = sig + } + + t.Run("not found", func(t *testing.T) { + tab := newTestBuilder(t) + for _, sig := range smeshers { + tab.Register(sig) + tab.mclock.EXPECT().CurrentLayer().Return(layer) + require.NoError(t, tab.Regossip(context.Background(), sig.NodeID())) + } + }) + + t.Run("success", func(t *testing.T) { + tab := newTestBuilder(t) + var refAtx *types.VerifiedActivationTx + + for _, sig := range smeshers { + tab.Register(sig) + + atx := newActivationTx(t, + sig, 0, types.EmptyATXID, types.EmptyATXID, nil, + layer.GetEpoch(), 0, 1, types.Address{}, 1, &types.NIPost{}) + require.NoError(t, atxs.Add(tab.cdb.Database, atx)) + + if refAtx == nil { + refAtx = atx + } + } + + blob, err := atxs.GetBlob(tab.cdb.Database, refAtx.ID().Bytes()) + require.NoError(t, err) + + // atx will be regossiped once (by the smesher) + tab.mclock.EXPECT().CurrentLayer().Return(layer) + ctx := context.Background() + tab.mpub.EXPECT().Publish(ctx, pubsub.AtxProtocol, blob) + require.NoError(t, tab.Regossip(ctx, refAtx.SmesherID)) + }) + + t.Run("checkpointed", func(t *testing.T) { + tab := newTestBuilder(t) + for _, sig := range smeshers { + tab.Register(sig) + + require.NoError(t, atxs.AddCheckpointed(tab.cdb.Database, + &atxs.CheckpointAtx{ID: types.RandomATXID(), Epoch: layer.GetEpoch(), SmesherID: sig.NodeID()})) + + tab.mclock.EXPECT().CurrentLayer().Return(layer) + require.NoError(t, tab.Regossip(context.Background(), sig.NodeID())) + } + }) +} + +func Test_Builder_Multi_InitialPost(t *testing.T) { + tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + smeshers := make(map[types.NodeID]*signing.EdSigner, 10) + for i := 0; i < 10; i++ { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + tab.Register(sig) + smeshers[sig.NodeID()] = sig + } + + var eg errgroup.Group + for _, sig := range smeshers { + sig := sig + eg.Go(func() error { + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).Return( + &types.Post{Indices: make([]byte, 10)}, + &types.PostInfo{ + CommitmentATX: types.RandomATXID(), + Nonce: new(types.VRFPostIndex), + }, + nil, + ) + tab.mValidator.EXPECT(). + Post(gomock.Any(), sig.NodeID(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + AnyTimes(). + Return(nil) + + require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) + + // postClient.Proof() should not be called again + require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) + return nil + }) + } + + eg.Wait() +} diff --git a/activation/activation_test.go b/activation/activation_test.go index b6a7f407cf..1febbe257a 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -376,7 +376,7 @@ func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(sig, prevAtx) + require.NoError(t, SignAndFinalizeAtx(sig, prevAtx)) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -392,6 +392,7 @@ func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { tab.mclock.EXPECT().CurrentLayer().Return(currLayer).Times(4) atx2, err := publishAtx(t, tab, sig.NodeID(), atx1.PublishEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) + require.NotNil(t, atx2) require.NotEqual(t, atx1, atx2) require.Equal(t, atx1.TargetEpoch()+1, atx2.TargetEpoch()) @@ -421,7 +422,7 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(sig, prevAtx) + require.NoError(t, SignAndFinalizeAtx(sig, prevAtx)) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -478,7 +479,7 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(sig, prevAtx) + require.NoError(t, SignAndFinalizeAtx(sig, prevAtx)) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -572,7 +573,7 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(challenge, nipostData.NIPost, posEpoch.Uint32(), types.Address{}) - SignAndFinalizeAtx(sig, prevAtx) + require.NoError(t, SignAndFinalizeAtx(sig, prevAtx)) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -661,7 +662,7 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) prevAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(sig, prevAtx) + require.NoError(t, SignAndFinalizeAtx(sig, prevAtx)) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -732,7 +733,7 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi CommitmentATX: nil, } posAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(sig, posAtx) + require.NoError(t, SignAndFinalizeAtx(sig, posAtx)) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPosAtx)) @@ -769,7 +770,7 @@ func TestBuilder_PublishActivationTx_NoPrevATX(t *testing.T) { otherSigner, err := signing.NewEdSigner() require.NoError(t, err) posAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(otherSigner, posAtx) + require.NoError(t, SignAndFinalizeAtx(otherSigner, posAtx)) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPosAtx)) @@ -812,7 +813,7 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve otherSigner, err := signing.NewEdSigner() require.NoError(t, err) posAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(otherSigner, posAtx) + require.NoError(t, SignAndFinalizeAtx(otherSigner, posAtx)) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPosAtx)) @@ -902,7 +903,7 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { poetBytes := []byte("66666") nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetBytes) posAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(otherSigner, posAtx) + require.NoError(t, SignAndFinalizeAtx(otherSigner, posAtx)) vPosAtx, err := posAtx.Verify(0, 2) r.NoError(err) r.NoError(atxs.Add(tab.cdb, vPosAtx)) @@ -917,7 +918,7 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { challenge.InitialPost = initialPost prevAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) prevAtx.InitialPost = initialPost - SignAndFinalizeAtx(sig, prevAtx) + require.NoError(t, SignAndFinalizeAtx(sig, prevAtx)) vPrevAtx, err := prevAtx.Verify(0, 1) r.NoError(err) r.NoError(atxs.Add(tab.cdb, vPrevAtx)) @@ -1019,7 +1020,7 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { poetBytes := []byte("66666") nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetBytes) posAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(otherSigner, posAtx) + require.NoError(t, SignAndFinalizeAtx(otherSigner, posAtx)) vPosAtx, err := posAtx.Verify(0, 1) r.NoError(err) r.NoError(atxs.Add(tab.cdb, vPosAtx)) @@ -1114,7 +1115,7 @@ func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { } nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), []byte("66666")) posAtx := newAtx(ch, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(sig, posAtx) + require.NoError(t, SignAndFinalizeAtx(sig, posAtx)) vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPosAtx)) @@ -1126,8 +1127,8 @@ func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { genesis := time.Now().Add(-time.Duration(currLayer) * layerDuration) return genesis.Add(layerDuration * time.Duration(got)) }).AnyTimes() - nipostErr := fmt.Errorf("NIPost builder error") - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nipostErr) + nipostErr := errors.New("NIPost builder error") + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), sig, gomock.Any()).Return(nil, nipostErr) require.ErrorIs(t, tab.PublishActivationTx(context.Background(), sig), nipostErr) // state is preserved @@ -1190,9 +1191,6 @@ func TestBuilder_SignAtx(t *testing.T) { sig, err := signing.NewEdSigner() require.NoError(t, err) - tab := newTestBuilder(t) - tab.Register(sig) - prevAtx := types.ATXID(types.HexToHash32("0x111")) challenge := types.NIPostChallenge{ Sequence: 1, @@ -1242,7 +1240,7 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { poetBytes := []byte("66666") nipostData := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetBytes) prevAtx := newAtx(challenge, nipostData.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(sig, prevAtx) + require.NoError(t, SignAndFinalizeAtx(sig, prevAtx)) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -1379,7 +1377,7 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { poetByte := []byte("66666") nipost := newNIPostWithChallenge(t, types.HexToHash32("55555"), poetByte) prevAtx := newAtx(challenge, nipost.NIPost, 2, types.Address{}) - SignAndFinalizeAtx(sig, prevAtx) + require.NoError(t, SignAndFinalizeAtx(sig, prevAtx)) vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.cdb, vPrevAtx)) @@ -1484,51 +1482,6 @@ func TestWaitPositioningAtx(t *testing.T) { } } -func TestRegossip(t *testing.T) { - layer := types.LayerID(10) - t.Run("not found", func(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t) - tab.Register(sig) - - tab.mclock.EXPECT().CurrentLayer().Return(layer) - require.NoError(t, tab.Regossip(context.Background(), sig.NodeID())) - }) - t.Run("success", func(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t) - tab.Register(sig) - - atx := newActivationTx(t, - sig, 0, types.EmptyATXID, types.EmptyATXID, nil, - layer.GetEpoch(), 0, 1, types.Address{}, 1, &types.NIPost{}) - require.NoError(t, atxs.Add(tab.cdb.Database, atx)) - blob, err := atxs.GetBlob(tab.cdb.Database, atx.ID().Bytes()) - require.NoError(t, err) - tab.mclock.EXPECT().CurrentLayer().Return(layer) - - ctx := context.Background() - tab.mpub.EXPECT().Publish(ctx, pubsub.AtxProtocol, blob) - require.NoError(t, tab.Regossip(ctx, sig.NodeID())) - }) - t.Run("checkpointed", func(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t) - tab.Register(sig) - - require.NoError(t, atxs.AddCheckpointed(tab.cdb.Database, - &atxs.CheckpointAtx{ID: types.ATXID{1}, Epoch: layer.GetEpoch(), SmesherID: sig.NodeID()})) - tab.mclock.EXPECT().CurrentLayer().Return(layer) - require.NoError(t, tab.Regossip(context.Background(), sig.NodeID())) - }) -} - func TestWaitingToBuildNipostChallengeWithJitter(t *testing.T) { t.Run("before grace period", func(t *testing.T) { // ┌──grace period──┐ From 50575477fd3244deea7c72eb533e3a14521a51e5 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:47:37 +0000 Subject: [PATCH 09/26] Simplify tests --- activation/activation_multi_test.go | 82 ++++-------------- activation/activation_test.go | 129 ++++++++++------------------ blocks/certifier_test.go | 2 +- 3 files changed, 63 insertions(+), 150 deletions(-) diff --git a/activation/activation_multi_test.go b/activation/activation_multi_test.go index e6227e928e..955bf39d68 100644 --- a/activation/activation_multi_test.go +++ b/activation/activation_multi_test.go @@ -12,26 +12,16 @@ import ( "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/p2p/pubsub" - "github.com/spacemeshos/go-spacemesh/signing" "github.com/spacemeshos/go-spacemesh/sql" "github.com/spacemeshos/go-spacemesh/sql/atxs" "github.com/spacemeshos/go-spacemesh/sql/localsql/nipost" ) func Test_Builder_Multi_StartSmeshingCoinbase(t *testing.T) { - tab := newTestBuilder(t) + tab := newTestBuilder(t, 5) coinbase := types.Address{1, 1, 1} - smeshers := make(map[types.NodeID]*signing.EdSigner, 10) - for i := 0; i < 10; i++ { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab.Register(sig) - smeshers[sig.NodeID()] = sig - } - - for _, sig := range smeshers { + for _, sig := range tab.signers { tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() @@ -50,7 +40,7 @@ func Test_Builder_Multi_StartSmeshingCoinbase(t *testing.T) { // calling StartSmeshing more than once before calling StopSmeshing is an error require.ErrorContains(t, tab.StartSmeshing(coinbase), "already started") - for _, sig := range smeshers { + for _, sig := range tab.signers { tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) } require.NoError(t, tab.StopSmeshing(true)) @@ -58,16 +48,9 @@ func Test_Builder_Multi_StartSmeshingCoinbase(t *testing.T) { func Test_Builder_Multi_RestartSmeshing(t *testing.T) { getBuilder := func(t *testing.T) *Builder { - tab := newTestBuilder(t) - - smeshers := make(map[types.NodeID]*signing.EdSigner, 10) - for i := 0; i < 10; i++ { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab.Register(sig) - smeshers[sig.NodeID()] = sig + tab := newTestBuilder(t, 5) + for _, sig := range tab.signers { tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).AnyTimes().DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() @@ -112,7 +95,7 @@ func Test_Builder_Multi_RestartSmeshing(t *testing.T) { } func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { - tab := newTestBuilder(t) + tab := newTestBuilder(t, 5) atx := types.RandomATXID() refChallenge := &types.NIPostChallenge{ @@ -130,14 +113,7 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { return genesis.Add(layerDuration * time.Duration(got)) }).AnyTimes() - smeshers := make(map[types.NodeID]*signing.EdSigner, 10) - for i := 0; i < 10; i++ { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab.Register(sig) - smeshers[sig.NodeID()] = sig - + for _, sig := range tab.signers { tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() @@ -151,7 +127,7 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(false)) - for _, sig := range smeshers { + for _, sig := range tab.signers { challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) require.NoError(t, err) require.Equal(t, refChallenge, challenge) // challenge still present @@ -168,7 +144,7 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) - for _, sig := range smeshers { + for _, sig := range tab.signers { challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) require.Nil(t, challenge) // challenge deleted @@ -185,7 +161,7 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) // no-op - for _, sig := range smeshers { + for _, sig := range tab.signers { challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) require.Nil(t, challenge) // challenge still deleted @@ -195,30 +171,19 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { func TestRegossip(t *testing.T) { layer := types.LayerID(10) - smeshers := make(map[types.NodeID]*signing.EdSigner, 10) - for i := 0; i < 10; i++ { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - smeshers[sig.NodeID()] = sig - } - t.Run("not found", func(t *testing.T) { - tab := newTestBuilder(t) - for _, sig := range smeshers { - tab.Register(sig) + tab := newTestBuilder(t, 5) + for _, sig := range tab.signers { tab.mclock.EXPECT().CurrentLayer().Return(layer) require.NoError(t, tab.Regossip(context.Background(), sig.NodeID())) } }) t.Run("success", func(t *testing.T) { - tab := newTestBuilder(t) + tab := newTestBuilder(t, 5) var refAtx *types.VerifiedActivationTx - for _, sig := range smeshers { - tab.Register(sig) - + for _, sig := range tab.signers { atx := newActivationTx(t, sig, 0, types.EmptyATXID, types.EmptyATXID, nil, layer.GetEpoch(), 0, 1, types.Address{}, 1, &types.NIPost{}) @@ -240,10 +205,8 @@ func TestRegossip(t *testing.T) { }) t.Run("checkpointed", func(t *testing.T) { - tab := newTestBuilder(t) - for _, sig := range smeshers { - tab.Register(sig) - + tab := newTestBuilder(t, 5) + for _, sig := range tab.signers { require.NoError(t, atxs.AddCheckpointed(tab.cdb.Database, &atxs.CheckpointAtx{ID: types.RandomATXID(), Epoch: layer.GetEpoch(), SmesherID: sig.NodeID()})) @@ -254,18 +217,9 @@ func TestRegossip(t *testing.T) { } func Test_Builder_Multi_InitialPost(t *testing.T) { - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - smeshers := make(map[types.NodeID]*signing.EdSigner, 10) - for i := 0; i < 10; i++ { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab.Register(sig) - smeshers[sig.NodeID()] = sig - } - + tab := newTestBuilder(t, 5, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) var eg errgroup.Group - for _, sig := range smeshers { + for _, sig := range tab.signers { sig := sig eg.Go(func() error { tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).Return( diff --git a/activation/activation_test.go b/activation/activation_test.go index 1febbe257a..5fadd51874 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/codec" @@ -105,7 +106,7 @@ type testAtxBuilder struct { mValidator *MocknipostValidator } -func newTestBuilder(tb testing.TB, opts ...BuilderOption) *testAtxBuilder { +func newTestBuilder(tb testing.TB, numSigners int, opts ...BuilderOption) *testAtxBuilder { lg := logtest.New(tb) ctrl := gomock.NewController(tb) @@ -143,6 +144,13 @@ func newTestBuilder(tb testing.TB, opts ...BuilderOption) *testAtxBuilder { opts..., ) tab.Builder = b + + for i := 0; i < numSigners; i++ { + sig, err := signing.NewEdSigner() + require.NoError(tb, err) + tab.Register(sig) + } + return tab } @@ -216,11 +224,8 @@ func publishAtx( // ========== Tests ========== func Test_Builder_StartSmeshingCoinbase(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t) - tab.Register(sig) + tab := newTestBuilder(t, 1) + sig := maps.Values(tab.signers)[0] coinbase := types.Address{1, 1, 1} tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( @@ -246,11 +251,8 @@ func Test_Builder_StartSmeshingCoinbase(t *testing.T) { func TestBuilder_RestartSmeshing(t *testing.T) { getBuilder := func(t *testing.T) *Builder { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t) - tab.Register(sig) + tab := newTestBuilder(t, 1) + sig := maps.Values(tab.signers)[0] tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).AnyTimes().DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { @@ -295,11 +297,8 @@ func TestBuilder_RestartSmeshing(t *testing.T) { } func TestBuilder_StopSmeshing_Delete(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t) - tab.Register(sig) + tab := newTestBuilder(t, 1) + sig := maps.Values(tab.signers)[0] currLayer := (postGenesisEpoch + 1).FirstLayer() tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).AnyTimes() @@ -354,16 +353,13 @@ func TestBuilder_StopSmeshing_Delete(t *testing.T) { } func TestBuilder_StopSmeshing_failsWhenNotStarted(t *testing.T) { - tab := newTestBuilder(t) + tab := newTestBuilder(t, 1) require.ErrorContains(t, tab.StopSmeshing(true), "not started") } func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration})) + sig := maps.Values(tab.signers)[0] posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() @@ -405,11 +401,8 @@ func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { // failing with ErrATXChallengeExpired. func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { // Arrange - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] // current layer is too late to be able to build a nipost on time currLayer := (postGenesisEpoch + 1).FirstLayer() @@ -462,11 +455,8 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { } func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] posEpoch := postGenesisEpoch currLayer := postGenesisEpoch.FirstLayer() @@ -553,14 +543,11 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { } func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - poetCfg := PoetConfig{ PhaseShift: layerDuration * 4, } - tab := newTestBuilder(t, WithPoetConfig(poetCfg)) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(poetCfg)) + sig := maps.Values(tab.signers)[0] posEpoch := postGenesisEpoch currLayer := (postGenesisEpoch + 1).FirstLayer().Add(5) // late for poet round start @@ -645,11 +632,8 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi } func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] posEpoch := types.EpochID(2) currLayer := posEpoch.FirstLayer() @@ -751,11 +735,8 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi } func TestBuilder_PublishActivationTx_NoPrevATX(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() @@ -794,11 +775,8 @@ func TestBuilder_PublishActivationTx_NoPrevATX(t *testing.T) { } func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserved(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() @@ -875,11 +853,8 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { r := require.New(t) // Arrange - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] otherSigner, err := signing.NewEdSigner() r.NoError(err) @@ -999,11 +974,8 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { r := require.New(t) // Arrange - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] otherSigner, err := signing.NewEdSigner() r.NoError(err) @@ -1098,11 +1070,8 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { } func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] posEpoch := postGenesisEpoch currLayer := posEpoch.FirstLayer() @@ -1220,15 +1189,14 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { }, events.WithBuffer(100)) require.NoError(t, err) - sig, err := signing.NewEdSigner() - require.NoError(t, err) - retryInterval := 50 * time.Microsecond tab := newTestBuilder( t, + 1, WithPoetConfig(PoetConfig{PhaseShift: 150 * time.Millisecond}), WithPoetRetryInterval(retryInterval), ) + sig := maps.Values(tab.signers)[0] posEpoch := types.EpochID(0) challenge := types.NIPostChallenge{ Sequence: 1, @@ -1346,11 +1314,8 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { } func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).Return( &types.Post{Indices: make([]byte, 10)}, @@ -1402,11 +1367,8 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { } func TestBuilder_InitialPostIsPersisted(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) - tab.Register(sig) + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4})) + sig := maps.Values(tab.signers)[0] tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).Return( &types.Post{Indices: make([]byte, 10)}, @@ -1440,14 +1402,11 @@ func TestWaitPositioningAtx(t *testing.T) { } { tc := tc t.Run(tc.desc, func(t *testing.T) { - sig, err := signing.NewEdSigner() - require.NoError(t, err) - - tab := newTestBuilder(t, WithPoetConfig(PoetConfig{ + tab := newTestBuilder(t, 1, WithPoetConfig(PoetConfig{ PhaseShift: tc.shift, GracePeriod: tc.grace, })) - tab.Register(sig) + sig := maps.Values(tab.signers)[0] tab.mclock.EXPECT().CurrentLayer().Return(types.LayerID(0)).AnyTimes() tab.mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn(func(lid types.LayerID) time.Time { diff --git a/blocks/certifier_test.go b/blocks/certifier_test.go index 947607cd16..7a0d23f7e8 100644 --- a/blocks/certifier_test.go +++ b/blocks/certifier_test.go @@ -664,7 +664,7 @@ func Test_OldLayersPruned(t *testing.T) { func Test_CertifyIfEligible(t *testing.T) { numSigners := 3 - tc := newTestCertifier(t, 3) + tc := newTestCertifier(t, numSigners) b := generateBlock(t, tc.db) tc.mb.EXPECT().GetBeacon(b.LayerIndex.GetEpoch()).Return(types.RandomBeacon(), nil) From 8730af0ba7cbf51e021bdd14ea1bbcd9e24f2eaf Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:21:33 +0000 Subject: [PATCH 10/26] Add HappyPath test for multismesher --- activation/activation.go | 1 + activation/activation_multi_test.go | 261 ++++++++++++++++++++++++++++ activation/activation_test.go | 5 +- signing/signer.go | 11 ++ 4 files changed, 276 insertions(+), 2 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index e68de4653d..10630743c6 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -551,6 +551,7 @@ func (b *Builder) createAtx( zap.Stringer("pub_epoch", pubEpoch), zap.Stringer("pub_epoch_first_layer", pubEpoch.FirstLayer()), zap.Stringer("current_layer", b.layerClock.CurrentLayer()), + zap.Stringer("node_id", sig.NodeID()), ) select { case <-ctx.Done(): diff --git a/activation/activation_multi_test.go b/activation/activation_multi_test.go index 955bf39d68..b7c562a9ef 100644 --- a/activation/activation_multi_test.go +++ b/activation/activation_multi_test.go @@ -2,6 +2,7 @@ package activation import ( "context" + "math/rand" "testing" "time" @@ -10,6 +11,7 @@ import ( "go.uber.org/mock/gomock" "golang.org/x/sync/errgroup" + "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/p2p/pubsub" "github.com/spacemeshos/go-spacemesh/sql" @@ -245,3 +247,262 @@ func Test_Builder_Multi_InitialPost(t *testing.T) { eg.Wait() } + +func Test_Builder_Multi_HappyPath(t *testing.T) { + layerDuration := 2 * time.Second + tab := newTestBuilder(t, 3, WithPoetConfig(PoetConfig{PhaseShift: layerDuration * 4, CycleGap: layerDuration})) + tab.regossipInterval = 0 // disable regossip for testing + + // step 1: build initial posts + initialPostChan := make(chan struct{}) + initialPostStep := make(map[types.NodeID]chan struct{}) + initialPost := make(map[types.NodeID]*nipost.Post) + for _, sig := range tab.signers { + ch := make(chan struct{}) + initialPostStep[sig.NodeID()] = ch + + nipost := nipost.Post{ + Indices: types.RandomBytes(10), + Nonce: rand.Uint32(), + Pow: rand.Uint64(), + + NumUnits: 4, + CommitmentATX: types.RandomATXID(), + VRFNonce: types.VRFPostIndex(rand.Uint64()), + } + initialPost[sig.NodeID()] = &nipost + + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { + <-initialPostChan + close(ch) + post := &types.Post{ + Indices: nipost.Indices, + Nonce: nipost.Nonce, + Pow: nipost.Pow, + } + postInfo := &types.PostInfo{ + NumUnits: nipost.NumUnits, + CommitmentATX: nipost.CommitmentATX, + Nonce: &nipost.VRFNonce, + } + + return post, postInfo, nil + }, + ) + } + + // step 2: build nipost challenge + nipostChallengeChan := make(chan struct{}) + nipostChallengeStep := make(map[types.NodeID]chan struct{}) + poetRoundEnd := time.Now().Add(1 * time.Second).Add(-tab.poetCfg.PhaseShift) // poetRoundEnd is in 100ms + for _, sig := range tab.signers { + ch := make(chan struct{}) + nipostChallengeStep[sig.NodeID()] = ch + + tab.mclock.EXPECT().CurrentLayer().DoAndReturn( + func() types.LayerID { + <-nipostChallengeChan + return postGenesisEpoch.FirstLayer() + 1 + }, + ) + + // called twice per id + tab.mclock.EXPECT().LayerToTime(postGenesisEpoch.FirstLayer()).Return(poetRoundEnd).Times(2) + + // logged once per id + tab.mclock.EXPECT().CurrentLayer().DoAndReturn( + func() types.LayerID { + close(ch) + return postGenesisEpoch.FirstLayer() + 1 + }, + ) + } + + // step 3: create ATX + nipostChan := make(chan struct{}) + nipostStep := make(map[types.NodeID]chan struct{}) + nipostState := make(map[types.NodeID]*nipost.NIPostState) + for _, sig := range tab.signers { + ch := make(chan struct{}) + nipostStep[sig.NodeID()] = ch + + // deadline for create ATX + tab.mclock.EXPECT().LayerToTime(postGenesisEpoch.Add(2).FirstLayer()).DoAndReturn( + func(_ types.LayerID) time.Time { + <-nipostChan + return time.Now().Add(5 * time.Second) + }, + ) + + post := &types.Post{ + Indices: initialPost[sig.NodeID()].Indices, + Nonce: initialPost[sig.NodeID()].Nonce, + Pow: initialPost[sig.NodeID()].Pow, + } + ref := &types.NIPostChallenge{ + PublishEpoch: postGenesisEpoch + 1, + CommitmentATX: &initialPost[sig.NodeID()].CommitmentATX, + Sequence: 0, + PrevATXID: types.EmptyATXID, + PositioningATX: tab.goldenATXID, + InitialPost: post, + } + + state := &nipost.NIPostState{ + NIPost: &types.NIPost{ + Membership: types.MerkleProof{}, + Post: &types.Post{ + Indices: types.RandomBytes(10), + Nonce: rand.Uint32(), + Pow: rand.Uint64(), + }, + PostMetadata: &types.PostMetadata{ + LabelsPerUnit: 128, + Challenge: shared.ZeroChallenge, + }, + }, + NumUnits: 4, + VRFNonce: types.VRFPostIndex(rand.Uint64()), + } + nipostState[sig.NodeID()] = state + tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), sig, ref).Return(state, nil) + + // awaiting atx publication epoch log + tab.mclock.EXPECT().CurrentLayer().DoAndReturn( + func() types.LayerID { + close(ch) + return postGenesisEpoch.Add(1).FirstLayer() + }, + ) + } + + // step 4: build and broadcast atx + atxChan := make(chan struct{}) + atxStep := make(map[types.NodeID]chan struct{}) + atxs := make(map[types.NodeID]types.ActivationTx) + endChan := make(chan struct{}) + for _, sig := range tab.signers { + ch := make(chan struct{}) + atxStep[sig.NodeID()] = ch + + tab.mclock.EXPECT().AwaitLayer(postGenesisEpoch.Add(1).FirstLayer()).DoAndReturn( + func(_ types.LayerID) <-chan struct{} { + <-atxChan + ch := make(chan struct{}) + close(ch) + return ch + }, + ) + tab.mclock.EXPECT().CurrentLayer().Return(postGenesisEpoch.Add(1).FirstLayer()) + + tab.mpub.EXPECT().Publish(gomock.Any(), pubsub.AtxProtocol, gomock.Any()).DoAndReturn( + func(ctx context.Context, _ string, got []byte) error { + close(ch) + + var gotAtx types.ActivationTx + require.NoError(t, codec.Decode(got, &gotAtx)) + atxs[gotAtx.SmesherID] = gotAtx + return nil + }, + ) + + // shutdown builder + tab.mnipost.EXPECT().ResetState(sig.NodeID()).DoAndReturn( + func(_ types.NodeID) error { + <-endChan + return context.Canceled + }, + ) + } + + // start smeshing + require.NoError(t, tab.StartSmeshing(types.Address{})) + + close(initialPostChan) // signal initial post to complete + for id, ch := range initialPostStep { + select { + case <-ch: + case <-time.After(5 * time.Second): + require.FailNowf(t, "timed out waiting for initial post", "node %s", id) + } + } + + for _, sig := range tab.signers { + post, err := nipost.InitialPost(tab.localDB, sig.NodeID()) + require.NoError(t, err) + + require.Equal(t, initialPost[sig.NodeID()], post) + } + + close(nipostChallengeChan) + for id, ch := range nipostChallengeStep { + select { + case <-ch: + case <-time.After(5 * time.Second): + require.FailNowf(t, "timed out waiting for nipost challenge", "node %s", id) + } + } + + for _, sig := range tab.signers { + challenge, err := nipost.Challenge(tab.localDB, sig.NodeID()) + require.NoError(t, err) + + post := &types.Post{ + Indices: initialPost[sig.NodeID()].Indices, + Nonce: initialPost[sig.NodeID()].Nonce, + Pow: initialPost[sig.NodeID()].Pow, + } + ref := &types.NIPostChallenge{ + PublishEpoch: postGenesisEpoch + 1, + CommitmentATX: &initialPost[sig.NodeID()].CommitmentATX, + Sequence: 0, + PrevATXID: types.EmptyATXID, + PositioningATX: tab.goldenATXID, + InitialPost: post, + } + + require.Equal(t, ref, challenge) + } + + close(nipostChan) + for id, ch := range nipostStep { + select { + case <-ch: + case <-time.After(5 * time.Second): + require.FailNowf(t, "timed out waiting for nipost", "node %s", id) + } + } + + close(atxChan) + for id, ch := range atxStep { + select { + case <-ch: + case <-time.After(5 * time.Second): + require.FailNowf(t, "timed out waiting for atx publication", "node %s", id) + } + } + + for _, sig := range tab.signers { + atx := atxs[sig.NodeID()] + require.Equal(t, initialPost[sig.NodeID()].Nonce, atx.NIPostChallenge.InitialPost.Nonce) + require.Equal(t, initialPost[sig.NodeID()].Pow, atx.NIPostChallenge.InitialPost.Pow) + require.Equal(t, initialPost[sig.NodeID()].Indices, atx.NIPostChallenge.InitialPost.Indices) + + require.Equal(t, initialPost[sig.NodeID()].CommitmentATX, *atx.NIPostChallenge.CommitmentATX) + require.Equal(t, postGenesisEpoch+1, atx.NIPostChallenge.PublishEpoch) + require.Equal(t, types.EmptyATXID, atx.NIPostChallenge.PrevATXID) + require.Equal(t, tab.goldenATXID, atx.NIPostChallenge.PositioningATX) + require.Equal(t, uint64(0), atx.NIPostChallenge.Sequence) + + require.Equal(t, types.Address{}, atx.Coinbase) + require.Equal(t, nipostState[sig.NodeID()].NumUnits, atx.NumUnits) + require.Equal(t, nipostState[sig.NodeID()].NIPost, atx.NIPost) + require.Equal(t, sig.NodeID(), *atx.NodeID) + require.Equal(t, nipostState[sig.NodeID()].VRFNonce, *atx.VRFNonce) + } + + // stop smeshing + close(endChan) + require.NoError(t, tab.StopSmeshing(false)) +} diff --git a/activation/activation_test.go b/activation/activation_test.go index 5fadd51874..51606e193f 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "go.uber.org/zap/zapcore" "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" @@ -107,12 +108,12 @@ type testAtxBuilder struct { } func newTestBuilder(tb testing.TB, numSigners int, opts ...BuilderOption) *testAtxBuilder { - lg := logtest.New(tb) + lg := logtest.New(tb, zapcore.DebugLevel) ctrl := gomock.NewController(tb) tab := &testAtxBuilder{ cdb: datastore.NewCachedDB(sql.InMemory(), lg), - localDb: localsql.InMemory(), + localDb: localsql.InMemory(sql.WithConnections(numSigners)), goldenATXID: types.ATXID(types.HexToHash32("77777")), mpub: mocks.NewMockPublisher(ctrl), diff --git a/signing/signer.go b/signing/signer.go index 4184f207db..bfe2bc68ea 100644 --- a/signing/signer.go +++ b/signing/signer.go @@ -161,3 +161,14 @@ func (es *EdSigner) VRFSigner() *VRFSigner { func (es *EdSigner) Prefix() []byte { return es.prefix } + +func (es *EdSigner) Matches(x any) bool { + if other, ok := x.(*EdSigner); ok { + return bytes.Equal(es.priv, other.priv) + } + return false +} + +func (es *EdSigner) String() string { + return es.NodeID().ShortString() +} From 95c9430401943b09f114cc64216e748a75539b40 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:30:24 +0000 Subject: [PATCH 11/26] Prevent possible race --- activation/activation_multi_test.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/activation/activation_multi_test.go b/activation/activation_multi_test.go index b7c562a9ef..e2b6e6065c 100644 --- a/activation/activation_multi_test.go +++ b/activation/activation_multi_test.go @@ -3,6 +3,7 @@ package activation import ( "context" "math/rand" + "sync" "testing" "time" @@ -380,6 +381,7 @@ func Test_Builder_Multi_HappyPath(t *testing.T) { // step 4: build and broadcast atx atxChan := make(chan struct{}) atxStep := make(map[types.NodeID]chan struct{}) + var atxMtx sync.Mutex atxs := make(map[types.NodeID]types.ActivationTx) endChan := make(chan struct{}) for _, sig := range tab.signers { @@ -398,8 +400,8 @@ func Test_Builder_Multi_HappyPath(t *testing.T) { tab.mpub.EXPECT().Publish(gomock.Any(), pubsub.AtxProtocol, gomock.Any()).DoAndReturn( func(ctx context.Context, _ string, got []byte) error { - close(ch) - + atxMtx.Lock() + defer atxMtx.Unlock() var gotAtx types.ActivationTx require.NoError(t, codec.Decode(got, &gotAtx)) atxs[gotAtx.SmesherID] = gotAtx @@ -410,6 +412,7 @@ func Test_Builder_Multi_HappyPath(t *testing.T) { // shutdown builder tab.mnipost.EXPECT().ResetState(sig.NodeID()).DoAndReturn( func(_ types.NodeID) error { + close(ch) <-endChan return context.Canceled }, @@ -428,13 +431,6 @@ func Test_Builder_Multi_HappyPath(t *testing.T) { } } - for _, sig := range tab.signers { - post, err := nipost.InitialPost(tab.localDB, sig.NodeID()) - require.NoError(t, err) - - require.Equal(t, initialPost[sig.NodeID()], post) - } - close(nipostChallengeChan) for id, ch := range nipostChallengeStep { select { @@ -482,6 +478,7 @@ func Test_Builder_Multi_HappyPath(t *testing.T) { require.FailNowf(t, "timed out waiting for atx publication", "node %s", id) } } + close(endChan) for _, sig := range tab.signers { atx := atxs[sig.NodeID()] @@ -503,6 +500,5 @@ func Test_Builder_Multi_HappyPath(t *testing.T) { } // stop smeshing - close(endChan) require.NoError(t, tab.StopSmeshing(false)) } From fe4a371a22aa229ba7bad4905cbe671d20612361 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:41:18 +0000 Subject: [PATCH 12/26] Remove unneeded assertion --- activation/activation_multi_test.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/activation/activation_multi_test.go b/activation/activation_multi_test.go index e2b6e6065c..2f03112e23 100644 --- a/activation/activation_multi_test.go +++ b/activation/activation_multi_test.go @@ -440,27 +440,6 @@ func Test_Builder_Multi_HappyPath(t *testing.T) { } } - for _, sig := range tab.signers { - challenge, err := nipost.Challenge(tab.localDB, sig.NodeID()) - require.NoError(t, err) - - post := &types.Post{ - Indices: initialPost[sig.NodeID()].Indices, - Nonce: initialPost[sig.NodeID()].Nonce, - Pow: initialPost[sig.NodeID()].Pow, - } - ref := &types.NIPostChallenge{ - PublishEpoch: postGenesisEpoch + 1, - CommitmentATX: &initialPost[sig.NodeID()].CommitmentATX, - Sequence: 0, - PrevATXID: types.EmptyATXID, - PositioningATX: tab.goldenATXID, - InitialPost: post, - } - - require.Equal(t, ref, challenge) - } - close(nipostChan) for id, ch := range nipostStep { select { From f10704149604a92458803d5c2288f6819c382112 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Tue, 30 Jan 2024 14:20:08 +0000 Subject: [PATCH 13/26] Cleanup --- activation/activation.go | 6 +++--- activation/validation_test.go | 24 ++++++++---------------- signing/signer.go | 1 + 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index 10630743c6..5a1dddc3a3 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -66,8 +66,7 @@ type Config struct { // it is responsible for initializing post, receiving poet proof and orchestrating nipst. after which it will // calculate total weight and providing relevant view as proof. type Builder struct { - parentCtx context.Context - eg errgroup.Group + eg errgroup.Group accountLock sync.RWMutex coinbaseAccount types.Address @@ -83,11 +82,12 @@ type Builder struct { // as well as the fields below. smeshingMutex sync.Mutex signers map[types.NodeID]*signing.EdSigner - stop context.CancelFunc layerClock layerClock syncer syncer log *zap.Logger + parentCtx context.Context + stop context.CancelFunc poetCfg PoetConfig poetRetryInterval time.Duration } diff --git a/activation/validation_test.go b/activation/validation_test.go index 4eb5ffbc55..2c0579c64f 100644 --- a/activation/validation_test.go +++ b/activation/validation_test.go @@ -96,11 +96,10 @@ func Test_Validation_InitialNIPostChallenge(t *testing.T) { posAtxId := types.ATXID{1, 2, 3} commitmentAtxId := types.ATXID{5, 6, 7} - var PublishEpoch types.EpochID = 2 challenge := types.NIPostChallenge{ Sequence: 0, PrevATXID: types.EmptyATXID, - PublishEpoch: PublishEpoch, + PublishEpoch: 2, PositioningATX: posAtxId, CommitmentATX: &commitmentAtxId, } @@ -122,11 +121,10 @@ func Test_Validation_InitialNIPostChallenge(t *testing.T) { posAtxId := types.ATXID{1, 2, 3} - var PublishEpoch types.EpochID = types.LayerID(2).GetEpoch() challenge := types.NIPostChallenge{ Sequence: 0, PrevATXID: types.EmptyATXID, - PublishEpoch: PublishEpoch, + PublishEpoch: 0, PositioningATX: posAtxId, CommitmentATX: &goldenATXID, } @@ -144,11 +142,10 @@ func Test_Validation_InitialNIPostChallenge(t *testing.T) { posAtxId := types.ATXID{1, 2, 3} commitmentAtxId := types.ATXID{5, 6, 7} - var PublishEpoch types.EpochID = 1 challenge := types.NIPostChallenge{ Sequence: 0, PrevATXID: types.EmptyATXID, - PublishEpoch: PublishEpoch, + PublishEpoch: 1, PositioningATX: posAtxId, CommitmentATX: &commitmentAtxId, } @@ -187,11 +184,10 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - var PublishEpoch types.EpochID = 2 challenge := types.NIPostChallenge{ Sequence: 10, PrevATXID: prevAtxId, - PublishEpoch: PublishEpoch, + PublishEpoch: 2, PositioningATX: posAtxId, CommitmentATX: nil, } @@ -217,11 +213,10 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - var PublishEpoch types.EpochID = types.LayerID(1012).GetEpoch() challenge := types.NIPostChallenge{ Sequence: 10, PrevATXID: prevAtxId, - PublishEpoch: PublishEpoch, + PublishEpoch: 101, PositioningATX: posAtxId, CommitmentATX: nil, } @@ -243,11 +238,10 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - var PublishEpoch types.EpochID = types.LayerID(1012).GetEpoch() challenge := types.NIPostChallenge{ Sequence: 10, PrevATXID: prevAtxId, - PublishEpoch: PublishEpoch, + PublishEpoch: 101, PositioningATX: posAtxId, CommitmentATX: nil, } @@ -273,11 +267,10 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - var PublishEpoch types.EpochID = 2 challenge := types.NIPostChallenge{ Sequence: 10, PrevATXID: prevAtxId, - PublishEpoch: PublishEpoch, + PublishEpoch: 2, PositioningATX: posAtxId, CommitmentATX: nil, } @@ -303,11 +296,10 @@ func Test_Validation_NIPostChallenge(t *testing.T) { prevAtxId := types.ATXID{3, 2, 1} posAtxId := types.ATXID{1, 2, 3} - var PublishEpoch types.EpochID = 2 challenge := types.NIPostChallenge{ Sequence: 10, PrevATXID: prevAtxId, - PublishEpoch: PublishEpoch, + PublishEpoch: 2, PositioningATX: posAtxId, CommitmentATX: nil, } diff --git a/signing/signer.go b/signing/signer.go index bfe2bc68ea..93b07f20b5 100644 --- a/signing/signer.go +++ b/signing/signer.go @@ -162,6 +162,7 @@ func (es *EdSigner) Prefix() []byte { return es.prefix } +// Matches implements the gomock.Matcher interface for testing. func (es *EdSigner) Matches(x any) bool { if other, ok := x.(*EdSigner); ok { return bytes.Equal(es.priv, other.priv) From fc5f75c4c39abc56c627568cc9020c5f6e92b8d4 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Wed, 31 Jan 2024 09:43:09 +0000 Subject: [PATCH 14/26] Update API --- activation/activation.go | 8 ++++++-- activation/interface.go | 2 +- activation/mocks.go | 26 ++++++++++++------------ activation/nipost.go | 8 ++++---- activation/post_verifier_scaling_test.go | 7 ++++--- api/grpcserver/grpcserver_test.go | 9 ++++---- api/grpcserver/smesher_service.go | 11 +++++++++- events/events.go | 26 ++++++++++++++++++------ go.mod | 6 +++--- go.sum | 12 +++++------ 10 files changed, 72 insertions(+), 43 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index 5a1dddc3a3..7f12faf879 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -260,8 +260,12 @@ func (b *Builder) StopSmeshing(deleteFiles bool) error { } // SmesherID returns the ID of the smesher that created this activation. -func (b *Builder) SmesherID() types.NodeID { - return types.EmptyNodeID // TODO(mafa): API needs to be changed to support multiple smeshers +func (b *Builder) SmesherIDs() []types.NodeID { + res := make([]types.NodeID, 0, len(b.signers)) + for _, sig := range b.signers { + res = append(res, sig.NodeID()) + } + return res } func (b *Builder) buildInitialPost(ctx context.Context, nodeId types.NodeID) error { diff --git a/activation/interface.go b/activation/interface.go index c3a1bd81f1..dfe0b97cec 100644 --- a/activation/interface.go +++ b/activation/interface.go @@ -97,7 +97,7 @@ type SmeshingProvider interface { Smeshing() bool StartSmeshing(types.Address) error StopSmeshing(bool) error - SmesherID() types.NodeID + SmesherIDs() []types.NodeID Coinbase() types.Address SetCoinbase(coinbase types.Address) } diff --git a/activation/mocks.go b/activation/mocks.go index 1ea6a13cc3..de99f6e98c 100644 --- a/activation/mocks.go +++ b/activation/mocks.go @@ -1243,40 +1243,40 @@ func (c *SmeshingProviderSetCoinbaseCall) DoAndReturn(f func(types.Address)) *Sm return c } -// SmesherID mocks base method. -func (m *MockSmeshingProvider) SmesherID() types.NodeID { +// SmesherIDs mocks base method. +func (m *MockSmeshingProvider) SmesherIDs() []types.NodeID { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SmesherID") - ret0, _ := ret[0].(types.NodeID) + ret := m.ctrl.Call(m, "SmesherIDs") + ret0, _ := ret[0].([]types.NodeID) return ret0 } -// SmesherID indicates an expected call of SmesherID. -func (mr *MockSmeshingProviderMockRecorder) SmesherID() *SmeshingProviderSmesherIDCall { +// SmesherIDs indicates an expected call of SmesherIDs. +func (mr *MockSmeshingProviderMockRecorder) SmesherIDs() *SmeshingProviderSmesherIDsCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SmesherID", reflect.TypeOf((*MockSmeshingProvider)(nil).SmesherID)) - return &SmeshingProviderSmesherIDCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SmesherIDs", reflect.TypeOf((*MockSmeshingProvider)(nil).SmesherIDs)) + return &SmeshingProviderSmesherIDsCall{Call: call} } -// SmeshingProviderSmesherIDCall wrap *gomock.Call -type SmeshingProviderSmesherIDCall struct { +// SmeshingProviderSmesherIDsCall wrap *gomock.Call +type SmeshingProviderSmesherIDsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *SmeshingProviderSmesherIDCall) Return(arg0 types.NodeID) *SmeshingProviderSmesherIDCall { +func (c *SmeshingProviderSmesherIDsCall) Return(arg0 []types.NodeID) *SmeshingProviderSmesherIDsCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *SmeshingProviderSmesherIDCall) Do(f func() types.NodeID) *SmeshingProviderSmesherIDCall { +func (c *SmeshingProviderSmesherIDsCall) Do(f func() []types.NodeID) *SmeshingProviderSmesherIDsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *SmeshingProviderSmesherIDCall) DoAndReturn(f func() types.NodeID) *SmeshingProviderSmesherIDCall { +func (c *SmeshingProviderSmesherIDsCall) DoAndReturn(f func() []types.NodeID) *SmeshingProviderSmesherIDsCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/activation/nipost.go b/activation/nipost.go index c385063ce6..bfb8976747 100644 --- a/activation/nipost.go +++ b/activation/nipost.go @@ -126,7 +126,7 @@ func (nb *NIPostBuilder) Proof( select { case <-ctx.Done(): if started { - events.EmitPostFailure() + events.EmitPostFailure(nodeID) } return nil, nil, ctx.Err() case <-time.After(2 * time.Second): // Wait a few seconds and try connecting again @@ -142,7 +142,7 @@ func (nb *NIPostBuilder) Proof( } } if !started { - events.EmitPostStart(challenge) + events.EmitPostStart(nodeID, challenge) started = true } @@ -152,10 +152,10 @@ func (nb *NIPostBuilder) Proof( case errors.Is(err, ErrPostClientClosed): continue case err != nil: - events.EmitPostFailure() + events.EmitPostFailure(nodeID) return nil, nil, err default: // err == nil - events.EmitPostComplete(challenge) + events.EmitPostComplete(nodeID, challenge) return post, postInfo, err } } diff --git a/activation/post_verifier_scaling_test.go b/activation/post_verifier_scaling_test.go index 5915f66f98..15a37cc05a 100644 --- a/activation/post_verifier_scaling_test.go +++ b/activation/post_verifier_scaling_test.go @@ -12,6 +12,7 @@ import ( "go.uber.org/zap/zaptest" "golang.org/x/sync/errgroup" + "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/events" ) @@ -36,9 +37,9 @@ func TestAutoScaling(t *testing.T) { return nil }) - events.EmitPostStart(nil) - events.EmitPostComplete(nil) - events.EmitPostFailure() + events.EmitPostStart(types.EmptyNodeID, nil) + events.EmitPostComplete(types.EmptyNodeID, nil) + events.EmitPostFailure(types.EmptyNodeID) require.Eventually(t, done.Load, time.Second, 10*time.Millisecond) close(stop) diff --git a/api/grpcserver/grpcserver_test.go b/api/grpcserver/grpcserver_test.go index 07837d9f7b..cc181bcb6f 100644 --- a/api/grpcserver/grpcserver_test.go +++ b/api/grpcserver/grpcserver_test.go @@ -630,14 +630,15 @@ func TestSmesherService(t *testing.T) { require.Equal(t, int32(code.Code_OK), res.Status.Code) }) - t.Run("SmesherID", func(t *testing.T) { + t.Run("SmesherIDs", func(t *testing.T) { t.Parallel() c, ctx := setupSmesherService(t) nodeId := types.RandomNodeID() - c.smeshingProvider.EXPECT().SmesherID().Return(nodeId) - res, err := c.SmesherID(ctx, &emptypb.Empty{}) + c.smeshingProvider.EXPECT().SmesherIDs().Return([]types.NodeID{nodeId}) + res, err := c.SmesherIDs(ctx, &emptypb.Empty{}) require.NoError(t, err) - require.Equal(t, nodeId.Bytes(), res.PublicKey) + require.Equal(t, 1, len(res.PublicKeys)) + require.Equal(t, nodeId.Bytes(), res.PublicKeys[0]) }) t.Run("SetCoinbaseMissingArgs", func(t *testing.T) { diff --git a/api/grpcserver/smesher_service.go b/api/grpcserver/smesher_service.go index 4979bd1f47..6f68042449 100644 --- a/api/grpcserver/smesher_service.go +++ b/api/grpcserver/smesher_service.go @@ -140,7 +140,16 @@ func (s SmesherService) StopSmeshing( // SmesherID returns the smesher ID of this node. func (s SmesherService) SmesherID(context.Context, *emptypb.Empty) (*pb.SmesherIDResponse, error) { - return &pb.SmesherIDResponse{PublicKey: s.smeshingProvider.SmesherID().Bytes()}, nil + return &pb.SmesherIDResponse{PublicKey: types.EmptyNodeID.Bytes()}, nil +} + +func (s SmesherService) SmesherIDs(context.Context, *emptypb.Empty) (*pb.SmesherIDsResponse, error) { + ids := s.smeshingProvider.SmesherIDs() + res := &pb.SmesherIDsResponse{} + for _, id := range ids { + res.PublicKeys = append(res.PublicKeys, id.Bytes()) + } + return res, nil } // Coinbase returns the current coinbase setting of this node. diff --git a/events/events.go b/events/events.go index fcf02e6d51..890e7f6ad7 100644 --- a/events/events.go +++ b/events/events.go @@ -127,30 +127,44 @@ func EmitPostServiceStopped() { ) } -func EmitPostStart(challenge []byte) { +func EmitPostStart(nodeID types.NodeID, challenge []byte) { const help = "Node started PoST execution using the challenge from PoET." emitUserEvent( help, false, - &pb.Event_PostStart{PostStart: &pb.EventPostStart{Challenge: challenge}}, + &pb.Event_PostStart{ + PostStart: &pb.EventPostStart{ + Challenge: challenge, + Smesher: nodeID.Bytes(), + }, + }, ) } -func EmitPostComplete(challenge []byte) { +func EmitPostComplete(nodeID types.NodeID, challenge []byte) { const help = "Node finished PoST execution using PoET challenge." emitUserEvent( help, false, - &pb.Event_PostComplete{PostComplete: &pb.EventPostComplete{Challenge: challenge}}, + &pb.Event_PostComplete{ + PostComplete: &pb.EventPostComplete{ + Challenge: challenge, + Smesher: nodeID.Bytes(), + }, + }, ) } -func EmitPostFailure() { +func EmitPostFailure(nodeID types.NodeID) { const help = "Node failed PoST execution." emitUserEvent( help, true, - &pb.Event_PostComplete{PostComplete: &pb.EventPostComplete{}}, + &pb.Event_PostComplete{ + PostComplete: &pb.EventPostComplete{ + Smesher: nodeID.Bytes(), + }, + }, ) } diff --git a/go.mod b/go.mod index a8f0f27abd..0bd032eacc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.6 require ( cloud.google.com/go/storage v1.37.0 github.com/ALTree/bigfloat v0.2.0 - github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240104130649-f55576898805 + github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240130121751-5a08714d8838 github.com/cosmos/btcutil v1.0.5 github.com/go-llsqlite/crawshaw v0.5.0 github.com/gofrs/flock v0.8.1 @@ -36,7 +36,7 @@ require ( github.com/quic-go/quic-go v0.41.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/seehuhn/mt19937 v1.0.0 - github.com/spacemeshos/api/release/go v1.26.0 + github.com/spacemeshos/api/release/go v1.26.1-0.20240131094112-bf720cefb0df github.com/spacemeshos/economics v0.1.2 github.com/spacemeshos/fixed v0.1.1 github.com/spacemeshos/go-scale v1.1.12 @@ -52,7 +52,7 @@ require ( github.com/zeebo/blake3 v0.2.3 go.uber.org/mock v0.4.0 go.uber.org/zap v1.26.0 - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a golang.org/x/sync v0.6.0 golang.org/x/time v0.5.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe diff --git a/go.sum b/go.sum index e895422f2c..6130cb26d9 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,8 @@ github.com/c0mm4nd/go-ripemd v0.0.0-20200326052756-bd1759ad7d10/go.mod h1:mYPR+a github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240104130649-f55576898805 h1:QjGJQDB+rcJkUEPjn4z3Ur6DgJHBbGobwdOX2i4WQ14= -github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240104130649-f55576898805/go.mod h1:x11iCbZV6hzzSQWMq610B6Wl5Lg1dhwqcVfeiWQQnQQ= +github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240130121751-5a08714d8838 h1:f5Jyh/fAlM3W/zYbecj1BrmRjcN+DDYBiHS0VQJsUlo= +github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240130121751-5a08714d8838/go.mod h1:x11iCbZV6hzzSQWMq610B6Wl5Lg1dhwqcVfeiWQQnQQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -622,8 +622,8 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spacemeshos/api/release/go v1.26.0 h1:wop4OoID/2o8BEwNP/wypCIy//CDTnv14perIZo0WOU= -github.com/spacemeshos/api/release/go v1.26.0/go.mod h1:cQXfRiIRPc8c6bh9+VAK/GwD0zYCu7jKcos/cPaDYcI= +github.com/spacemeshos/api/release/go v1.26.1-0.20240131094112-bf720cefb0df h1:7bNTouylWV/zwiscddksTjs9pZ4TMqipfPrPiUno2h8= +github.com/spacemeshos/api/release/go v1.26.1-0.20240131094112-bf720cefb0df/go.mod h1:fK9RBD8eTVXHrqkkal2bwQB4N8M9sOhPs4rnVmWqEc0= github.com/spacemeshos/economics v0.1.2 h1:kw8cE5SMa/7svHOGorCd2w8ef1y8iP0p47/2VDOK8Ns= github.com/spacemeshos/economics v0.1.2/go.mod h1:ngeWn5E/jy9dJP1MHyuk3ehF8NBMTYhchqVDhAHUUNk= github.com/spacemeshos/fixed v0.1.1 h1:N1y4SUpq1EV+IdJrWJwUCt1oBFzeru/VKVcBsvPc2Fk= @@ -763,8 +763,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= From 20b968239317ec979e6795a94bb7276de3df7af4 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:04:29 +0000 Subject: [PATCH 15/26] Update API reference --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0bd032eacc..e7f1ca0525 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/quic-go/quic-go v0.41.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/seehuhn/mt19937 v1.0.0 - github.com/spacemeshos/api/release/go v1.26.1-0.20240131094112-bf720cefb0df + github.com/spacemeshos/api/release/go v1.27.1-0.20240131150253-2d68224f035f github.com/spacemeshos/economics v0.1.2 github.com/spacemeshos/fixed v0.1.1 github.com/spacemeshos/go-scale v1.1.12 diff --git a/go.sum b/go.sum index 6130cb26d9..16f63105f7 100644 --- a/go.sum +++ b/go.sum @@ -622,8 +622,8 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spacemeshos/api/release/go v1.26.1-0.20240131094112-bf720cefb0df h1:7bNTouylWV/zwiscddksTjs9pZ4TMqipfPrPiUno2h8= -github.com/spacemeshos/api/release/go v1.26.1-0.20240131094112-bf720cefb0df/go.mod h1:fK9RBD8eTVXHrqkkal2bwQB4N8M9sOhPs4rnVmWqEc0= +github.com/spacemeshos/api/release/go v1.27.1-0.20240131150253-2d68224f035f h1:fnwYkbHBNv4vXlsUG366eWwei2BdBbyZnuM3MjA6nkw= +github.com/spacemeshos/api/release/go v1.27.1-0.20240131150253-2d68224f035f/go.mod h1:fK9RBD8eTVXHrqkkal2bwQB4N8M9sOhPs4rnVmWqEc0= github.com/spacemeshos/economics v0.1.2 h1:kw8cE5SMa/7svHOGorCd2w8ef1y8iP0p47/2VDOK8Ns= github.com/spacemeshos/economics v0.1.2/go.mod h1:ngeWn5E/jy9dJP1MHyuk3ehF8NBMTYhchqVDhAHUUNk= github.com/spacemeshos/fixed v0.1.1 h1:N1y4SUpq1EV+IdJrWJwUCt1oBFzeru/VKVcBsvPc2Fk= From ef0e6fb93826d4913fff71eceb8d1757db06788b Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 1 Feb 2024 09:59:48 +0000 Subject: [PATCH 16/26] Return unimplemented on deprecated GRPC call --- api/grpcserver/smesher_service.go | 2 +- api/grpcserver/smesher_service_test.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/api/grpcserver/smesher_service.go b/api/grpcserver/smesher_service.go index 6f68042449..63ea57f6d2 100644 --- a/api/grpcserver/smesher_service.go +++ b/api/grpcserver/smesher_service.go @@ -140,7 +140,7 @@ func (s SmesherService) StopSmeshing( // SmesherID returns the smesher ID of this node. func (s SmesherService) SmesherID(context.Context, *emptypb.Empty) (*pb.SmesherIDResponse, error) { - return &pb.SmesherIDResponse{PublicKey: types.EmptyNodeID.Bytes()}, nil + return nil, status.Errorf(codes.Unimplemented, "this endpoint has been deprecated, use `SmesherIDs` instead") } func (s SmesherService) SmesherIDs(context.Context, *emptypb.Empty) (*pb.SmesherIDsResponse, error) { diff --git a/api/grpcserver/smesher_service_test.go b/api/grpcserver/smesher_service_test.go index c9d4afc1c7..a5812b05a3 100644 --- a/api/grpcserver/smesher_service_test.go +++ b/api/grpcserver/smesher_service_test.go @@ -10,6 +10,8 @@ import ( "github.com/spacemeshos/post/config" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" "github.com/spacemeshos/go-spacemesh/activation" @@ -228,3 +230,22 @@ func TestSmesherService_PostSetupStatus(t *testing.T) { require.False(t, resp.Status.Opts.Throttle) }) } + +func TestSmesherService_SmesherID(t *testing.T) { + ctrl := gomock.NewController(t) + smeshingProvider := activation.NewMockSmeshingProvider(ctrl) + postSupervisor := grpcserver.NewMockpostSupervisor(ctrl) + svc := grpcserver.NewSmesherService( + smeshingProvider, + postSupervisor, + time.Second, + activation.DefaultPostSetupOpts(), + ) + + resp, err := svc.SmesherID(context.Background(), &emptypb.Empty{}) + require.Error(t, err) + require.Nil(t, resp) + statusErr, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, codes.Unimplemented, statusErr.Code()) +} From cf39e02b3082c9f79bf0c1190bc28bb0e28e9f47 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:34:30 +0000 Subject: [PATCH 17/26] Update api dependency --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e7f1ca0525..ab96819293 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/quic-go/quic-go v0.41.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/seehuhn/mt19937 v1.0.0 - github.com/spacemeshos/api/release/go v1.27.1-0.20240131150253-2d68224f035f + github.com/spacemeshos/api/release/go v1.28.0 github.com/spacemeshos/economics v0.1.2 github.com/spacemeshos/fixed v0.1.1 github.com/spacemeshos/go-scale v1.1.12 diff --git a/go.sum b/go.sum index 16f63105f7..844a4eb094 100644 --- a/go.sum +++ b/go.sum @@ -622,8 +622,8 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spacemeshos/api/release/go v1.27.1-0.20240131150253-2d68224f035f h1:fnwYkbHBNv4vXlsUG366eWwei2BdBbyZnuM3MjA6nkw= -github.com/spacemeshos/api/release/go v1.27.1-0.20240131150253-2d68224f035f/go.mod h1:fK9RBD8eTVXHrqkkal2bwQB4N8M9sOhPs4rnVmWqEc0= +github.com/spacemeshos/api/release/go v1.28.0 h1:HmrNf0kV7U9o2rwqrmZ0tfPgQA8JQWY8EYGoiCB4bnI= +github.com/spacemeshos/api/release/go v1.28.0/go.mod h1:fK9RBD8eTVXHrqkkal2bwQB4N8M9sOhPs4rnVmWqEc0= github.com/spacemeshos/economics v0.1.2 h1:kw8cE5SMa/7svHOGorCd2w8ef1y8iP0p47/2VDOK8Ns= github.com/spacemeshos/economics v0.1.2/go.mod h1:ngeWn5E/jy9dJP1MHyuk3ehF8NBMTYhchqVDhAHUUNk= github.com/spacemeshos/fixed v0.1.1 h1:N1y4SUpq1EV+IdJrWJwUCt1oBFzeru/VKVcBsvPc2Fk= From 1a6bbae6023609a4299a8d2973856b43cee0eb76 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:58:56 +0000 Subject: [PATCH 18/26] Review feedback --- common/types/hashes.go | 12 ++++-------- common/types/nodeid.go | 5 ++--- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/common/types/hashes.go b/common/types/hashes.go index f0ff52d9d5..3d1aae2484 100644 --- a/common/types/hashes.go +++ b/common/types/hashes.go @@ -47,11 +47,9 @@ func (h Hash20) String() string { return h.Hex() } -// ShortString returns a the first 10 characters of the hash, for logging purposes. +// ShortString returns a the first 5 hex-encoded bytes of the hash, for logging purposes. func (h Hash20) ShortString() string { - str := h.Hex() - l := len(str) - return fmt.Sprintf("%.10s", str[min(2, l):]) + return hex.EncodeToString(h[:5]) } // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, @@ -179,11 +177,9 @@ func (h Hash32) String() string { return h.ShortString() } -// ShortString returns the first 10 characters of the hash, for logging purposes. +// ShortString returns the first 5 hex-encoded bytes of the hash, for logging purposes. func (h Hash32) ShortString() string { - str := h.Hex() - l := len(str) - return fmt.Sprintf("%.10s", str[min(2, l):]) + return hex.EncodeToString(h[:5]) } // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, diff --git a/common/types/nodeid.go b/common/types/nodeid.go index ca5614c6f8..13d16aaf2a 100644 --- a/common/types/nodeid.go +++ b/common/types/nodeid.go @@ -2,7 +2,6 @@ package types import ( "encoding/hex" - "fmt" "github.com/spacemeshos/go-scale" @@ -35,9 +34,9 @@ func (id NodeID) Bytes() []byte { return id[:] } -// ShortString returns a the first 5 characters of the ID, for logging purposes. +// ShortString returns a the first 3 hex-encoded bytes of the ID, for logging purposes. func (id NodeID) ShortString() string { - return fmt.Sprintf("%.5s", id.String()) + return hex.EncodeToString(id[:3]) } // Field returns a log field. Implements the LoggableField interface. From 33572618566916e1f0affe93fb8c544a7a67b38f Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:27:08 +0000 Subject: [PATCH 19/26] Review feedback --- activation/activation_multi_test.go | 28 +++++++-------- activation/activation_test.go | 55 +++++++++++++++++------------ activation/post_test.go | 1 - 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/activation/activation_multi_test.go b/activation/activation_multi_test.go index 2f03112e23..b9ac03f158 100644 --- a/activation/activation_multi_test.go +++ b/activation/activation_multi_test.go @@ -31,10 +31,6 @@ func Test_Builder_Multi_StartSmeshingCoinbase(t *testing.T) { return nil, nil, ctx.Err() }) } - tab.mValidator.EXPECT(). - Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - AnyTimes(). - Return(nil) tab.mclock.EXPECT().CurrentLayer().Return(types.LayerID(0)).AnyTimes() tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).AnyTimes() require.NoError(t, tab.StartSmeshing(coinbase)) @@ -98,7 +94,8 @@ func Test_Builder_Multi_RestartSmeshing(t *testing.T) { } func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { - tab := newTestBuilder(t, 5) + numIds := 5 + tab := newTestBuilder(t, numIds) atx := types.RandomATXID() refChallenge := &types.NIPostChallenge{ @@ -107,14 +104,8 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { } currLayer := (postGenesisEpoch + 1).FirstLayer() - tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).AnyTimes() - tab.mclock.EXPECT().CurrentLayer().Return(currLayer).AnyTimes() - tab.mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( - func(got types.LayerID) time.Time { - // time.Now() ~= currentLayer - genesis := time.Now().Add(-time.Duration(currLayer) * layerDuration) - return genesis.Add(layerDuration * time.Duration(got)) - }).AnyTimes() + tab.mclock.EXPECT().CurrentLayer().Return(currLayer).Times(numIds) + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).Times(numIds) for _, sig := range tab.signers { tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( @@ -129,6 +120,10 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(false)) + require.True(t, tab.mctrl.Satisfied(), "failed to assert all mocks were called the expected number of times") + + tab.mclock.EXPECT().CurrentLayer().Return(currLayer).Times(numIds) + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).Times(numIds) for _, sig := range tab.signers { challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) @@ -136,7 +131,6 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { require.Equal(t, refChallenge, challenge) // challenge still present tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) - tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() @@ -146,6 +140,10 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) + require.True(t, tab.mctrl.Satisfied(), "failed to assert all mocks were called the expected number of times") + + tab.mclock.EXPECT().CurrentLayer().Return(currLayer).Times(numIds) + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).Times(numIds) for _, sig := range tab.signers { challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) @@ -153,7 +151,6 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { require.Nil(t, challenge) // challenge deleted tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) - tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() @@ -163,6 +160,7 @@ func Test_Builder_Multi_StopSmeshing_Delete(t *testing.T) { require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) // no-op + require.True(t, tab.mctrl.Satisfied(), "failed to assert all mocks were called the expected number of times") for _, sig := range tab.signers { challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) diff --git a/activation/activation_test.go b/activation/activation_test.go index 51606e193f..3f54e502a7 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -99,6 +99,7 @@ type testAtxBuilder struct { localDb *localsql.Database goldenATXID types.ATXID + mctrl *gomock.Controller mpub *mocks.MockPublisher mnipost *MocknipostBuilder mpostClient *MockPostClient @@ -116,6 +117,7 @@ func newTestBuilder(tb testing.TB, numSigners int, opts ...BuilderOption) *testA localDb: localsql.InMemory(sql.WithConnections(numSigners)), goldenATXID: types.ATXID(types.HexToHash32("77777")), + mctrl: ctrl, mpub: mocks.NewMockPublisher(ctrl), mnipost: NewMocknipostBuilder(ctrl), mpostClient: NewMockPostClient(ctrl), @@ -234,10 +236,6 @@ func Test_Builder_StartSmeshingCoinbase(t *testing.T) { <-ctx.Done() return nil, nil, ctx.Err() }) - tab.mValidator.EXPECT(). - Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - AnyTimes(). - Return(nil) tab.mclock.EXPECT().CurrentLayer().Return(types.LayerID(0)).AnyTimes() tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).AnyTimes() require.NoError(t, tab.StartSmeshing(coinbase)) @@ -301,52 +299,63 @@ func TestBuilder_StopSmeshing_Delete(t *testing.T) { tab := newTestBuilder(t, 1) sig := maps.Values(tab.signers)[0] + atx := types.RandomATXID() + refChallenge := &types.NIPostChallenge{ + PublishEpoch: postGenesisEpoch + 2, + CommitmentATX: &atx, + } + currLayer := (postGenesisEpoch + 1).FirstLayer() - tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})).AnyTimes() - tab.mclock.EXPECT().CurrentLayer().Return(currLayer).AnyTimes() - tab.mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( - func(got types.LayerID) time.Time { - // time.Now() ~= currentLayer - genesis := time.Now().Add(-time.Duration(currLayer) * layerDuration) - return genesis.Add(layerDuration * time.Duration(got)) - }).AnyTimes() - tab.mnipost.EXPECT().BuildNIPost(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, _ *signing.EdSigner, _ *types.NIPostChallenge) (*nipost.NIPostState, error) { - <-ctx.Done() - return nil, ctx.Err() - }). - AnyTimes() + tab.mclock.EXPECT().CurrentLayer().Return(currLayer) + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})) + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { <-ctx.Done() return nil, nil, ctx.Err() - }).AnyTimes() + }) // add challenge to DB - refChallenge := &types.NIPostChallenge{ - PublishEpoch: postGenesisEpoch + 2, - CommitmentATX: &types.ATXID{1, 2, 3}, - } require.NoError(t, nipost.AddChallenge(tab.localDb, sig.NodeID(), refChallenge)) require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(false)) + require.True(t, tab.mctrl.Satisfied(), "failed to assert all mocks were called the expected number of times") + + tab.mclock.EXPECT().CurrentLayer().Return(currLayer) + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})) challenge, err := nipost.Challenge(tab.localDb, sig.NodeID()) require.NoError(t, err) require.Equal(t, refChallenge, challenge) // challenge still present tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { + <-ctx.Done() + return nil, nil, ctx.Err() + }) + require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) + require.True(t, tab.mctrl.Satisfied(), "failed to assert all mocks were called the expected number of times") + + tab.mclock.EXPECT().CurrentLayer().Return(currLayer) + tab.mclock.EXPECT().AwaitLayer(gomock.Any()).Return(make(chan struct{})) challenge, err = nipost.Challenge(tab.localDb, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) require.Nil(t, challenge) // challenge deleted tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) + tab.mnipost.EXPECT().Proof(gomock.Any(), sig.NodeID(), shared.ZeroChallenge).DoAndReturn( + func(ctx context.Context, _ types.NodeID, _ []byte) (*types.Post, *types.PostInfo, error) { + <-ctx.Done() + return nil, nil, ctx.Err() + }) require.NoError(t, tab.StartSmeshing(types.Address{})) require.NoError(t, tab.StopSmeshing(true)) // no-op + require.True(t, tab.mctrl.Satisfied(), "failed to assert all mocks were called the expected number of times") challenge, err = nipost.Challenge(tab.localDb, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) diff --git a/activation/post_test.go b/activation/post_test.go index 1a67e30317..617f9996f6 100644 --- a/activation/post_test.go +++ b/activation/post_test.go @@ -279,7 +279,6 @@ func TestPostSetupManager_findCommitmentAtx_UsesLatestAtx(t *testing.T) { PublishEpoch: 1, } atx := types.NewActivationTx(challenge, types.Address{}, nil, 2, nil) - atx.SetEffectiveNumUnits(2) require.NoError(t, SignAndFinalizeAtx(mgr.signer, atx)) atx.SetEffectiveNumUnits(atx.NumUnits) atx.SetReceived(time.Now()) From a8b5e805d55cf2bae7574823656c7c686e22abd8 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:45:13 +0000 Subject: [PATCH 20/26] Continue reset when one ID fails --- activation/activation.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index 7f12faf879..9821b21544 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -243,17 +243,21 @@ func (b *Builder) StopSmeshing(deleteFiles bool) error { if !deleteFiles { return nil } + var resetErr error for _, sig := range b.signers { if err := b.nipostBuilder.ResetState(sig.NodeID()); err != nil { - b.log.Error("failed to delete builder state", zap.Error(err)) - return err + b.log.Error("failed to reset builder state", log.ZShortStringer("nodeId", sig.NodeID()), zap.Error(err)) + err = fmt.Errorf("reset builder state for id %s: %w", sig.NodeID().ShortString(), err) + resetErr = errors.Join(resetErr, err) + continue } if err := nipost.RemoveChallenge(b.localDB, sig.NodeID()); err != nil { b.log.Error("failed to remove nipost challenge", zap.Error(err)) - return err + err = fmt.Errorf("remove nipost challenge for id %s: %w", sig.NodeID().ShortString(), err) + resetErr = errors.Join(resetErr, err) } } - return nil + return resetErr default: return fmt.Errorf("failed to stop smeshing: %w", err) } From 7fe8fc77f02651410c5f4b210e94db79c2392e6a Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:51:48 +0000 Subject: [PATCH 21/26] Review feedback --- activation/activation.go | 44 +++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index 9821b21544..13e1e25946 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -11,6 +11,7 @@ import ( "github.com/spacemeshos/post/shared" "go.uber.org/zap" + "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/activation/metrics" @@ -66,30 +67,28 @@ type Config struct { // it is responsible for initializing post, receiving poet proof and orchestrating nipst. after which it will // calculate total weight and providing relevant view as proof. type Builder struct { - eg errgroup.Group - - accountLock sync.RWMutex - coinbaseAccount types.Address - goldenATXID types.ATXID - regossipInterval time.Duration - cdb *datastore.CachedDB - localDB *localsql.Database - publisher pubsub.Publisher - nipostBuilder nipostBuilder - validator nipostValidator - - // smeshingMutex protects `StartSmeshing` and `StopSmeshing` from concurrent access - // as well as the fields below. - smeshingMutex sync.Mutex - signers map[types.NodeID]*signing.EdSigner - + accountLock sync.RWMutex + coinbaseAccount types.Address + goldenATXID types.ATXID + regossipInterval time.Duration + cdb *datastore.CachedDB + localDB *localsql.Database + publisher pubsub.Publisher + nipostBuilder nipostBuilder + validator nipostValidator layerClock layerClock syncer syncer log *zap.Logger parentCtx context.Context - stop context.CancelFunc poetCfg PoetConfig poetRetryInterval time.Duration + + // smeshingMutex protects methods like `StartSmeshing` and `StopSmeshing` from concurrent execution + // since they (can) modify the fields below. + smeshingMutex sync.Mutex + signers map[types.NodeID]*signing.EdSigner + eg errgroup.Group + stop context.CancelFunc } // BuilderOption ... @@ -237,6 +236,7 @@ func (b *Builder) StopSmeshing(deleteFiles bool) error { b.stop() err := b.eg.Wait() + b.eg = errgroup.Group{} b.stop = nil switch { case err == nil || errors.Is(err, context.Canceled): @@ -265,11 +265,9 @@ func (b *Builder) StopSmeshing(deleteFiles bool) error { // SmesherID returns the ID of the smesher that created this activation. func (b *Builder) SmesherIDs() []types.NodeID { - res := make([]types.NodeID, 0, len(b.signers)) - for _, sig := range b.signers { - res = append(res, sig.NodeID()) - } - return res + b.smeshingMutex.Lock() + defer b.smeshingMutex.Unlock() + return maps.Keys(b.signers) } func (b *Builder) buildInitialPost(ctx context.Context, nodeId types.NodeID) error { From d0fa77c1e81fed5c95308c10476d699f27fac9d7 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Fri, 2 Feb 2024 09:28:14 +0000 Subject: [PATCH 22/26] Add e2e test --- activation/e2e/nipost_test.go | 122 ++++++++++++++++++++++++++++++++++ activation/poet.go | 4 -- 2 files changed, 122 insertions(+), 4 deletions(-) diff --git a/activation/e2e/nipost_test.go b/activation/e2e/nipost_test.go index 9583abbfe4..b9c778d10e 100644 --- a/activation/e2e/nipost_test.go +++ b/activation/e2e/nipost_test.go @@ -350,3 +350,125 @@ func TestNewNIPostBuilderNotInitialized(t *testing.T) { ) require.NoError(t, err) } + +func Test_NIPostBuilderWithMultipleClients(t *testing.T) { + ctrl := gomock.NewController(t) + + signers := make(map[types.NodeID]*signing.EdSigner, 3) + for i := 0; i < 3; i++ { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + signers[sig.NodeID()] = sig + } + + logger := zaptest.NewLogger(t) + goldenATX := types.ATXID{2, 3, 4} + cfg := activation.DefaultPostConfig() + cdb := datastore.NewCachedDB(sql.InMemory(), log.NewFromLog(logger)) + + syncer := activation.NewMocksyncer(gomock.NewController(t)) + syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { + synced := make(chan struct{}) + close(synced) + return synced + }) + + svc := grpcserver.NewPostService(logger) + grpcCfg, cleanup := launchServer(t, svc) + t.Cleanup(cleanup) + + opts := activation.DefaultPostSetupOpts() + opts.ProviderID.SetUint32(initialization.CPUProviderID()) + opts.Scrypt.N = 2 // Speedup initialization in tests. + + var eg errgroup.Group + for _, sig := range signers { + sig := sig + opts := opts + eg.Go(func() error { + mgr, err := activation.NewPostSetupManager(sig.NodeID(), cfg, logger, cdb, goldenATX, syncer) + require.NoError(t, err) + + opts.DataDir = t.TempDir() + initPost(t, mgr, opts) + t.Cleanup(launchPostSupervisor(t, logger, mgr, grpcCfg, opts)) + + require.Eventually(t, func() bool { + _, err := svc.Client(sig.NodeID()) + return err == nil + }, 10*time.Second, 100*time.Millisecond, "timed out waiting for connection") + return nil + }) + } + require.NoError(t, eg.Wait()) + + // ensure that genesis aligns with layer timings + genesis := time.Now().Add(layerDuration).Round(layerDuration) + epoch := layersPerEpoch * layerDuration + poetCfg := activation.PoetConfig{ + PhaseShift: epoch / 2, + CycleGap: epoch / 4, + GracePeriod: epoch / 5, + RequestTimeout: epoch / 5, + RequestRetryDelay: epoch / 50, + MaxRequestRetries: 10, + } + poetProver := spawnPoet( + t, + WithGenesis(genesis), + WithEpochDuration(epoch), + WithPhaseShift(poetCfg.PhaseShift), + WithCycleGap(poetCfg.CycleGap), + ) + + mclock := activation.NewMocklayerClock(ctrl) + mclock.EXPECT().LayerToTime(gomock.Any()).AnyTimes().DoAndReturn( + func(got types.LayerID) time.Time { + return genesis.Add(layerDuration * time.Duration(got)) + }, + ) + + verifier, err := activation.NewPostVerifier(cfg, logger.Named("verifier")) + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) + + poetDb := activation.NewPoetDb(sql.InMemory(), log.NewFromLog(logger).Named("poetDb")) + + db := localsql.InMemory() + nb, err := activation.NewNIPostBuilder( + db, + poetDb, + svc, + []types.PoetServer{{Address: poetProver.RestURL().String()}}, + logger.Named("nipostBuilder"), + poetCfg, + mclock, + ) + require.NoError(t, err) + + challenge := types.NIPostChallenge{ + PublishEpoch: postGenesisEpoch + 2, + } + + for _, sig := range signers { + sig := sig + eg.Go(func() error { + nipost, err := nb.BuildNIPost(context.Background(), sig, &challenge) + require.NoError(t, err) + + v := activation.NewValidator(poetDb, cfg, opts.Scrypt, verifier) + _, err = v.NIPost( + context.Background(), + sig.NodeID(), + goldenATX, + nipost.NIPost, + challenge.Hash(), + nipost.NumUnits, + ) + require.NoError(t, err) + return nil + }) + } + require.NoError(t, eg.Wait()) +} diff --git a/activation/poet.go b/activation/poet.go index 31e2ea9ebd..0c83704083 100644 --- a/activation/poet.go +++ b/activation/poet.go @@ -221,10 +221,6 @@ func (c *HTTPPoetClient) Proof(ctx context.Context, roundID string) (*types.Poet RoundID: roundID, Statement: types.BytesToHash(statement), } - if c.poetServiceID == nil { - c.poetServiceID = proof.PoetServiceID - } - return &proof, members, nil } From 23cdcb5a6f62ec8f99c8750ab2552d5fce95e1ec Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Fri, 2 Feb 2024 15:38:31 +0000 Subject: [PATCH 23/26] Add e2e test for atxbuilder --- activation/activation.go | 7 - activation/activation_multi_test.go | 4 - activation/activation_test.go | 12 -- activation/e2e/activation_test.go | 206 ++++++++++++++++++++++++++++ activation/e2e/nipost_test.go | 36 ++--- node/node.go | 3 +- timesync/clock.go | 40 +++--- timesync/clock_options.go | 9 +- timesync/clock_test.go | 28 ++-- 9 files changed, 264 insertions(+), 81 deletions(-) create mode 100644 activation/e2e/activation_test.go diff --git a/activation/activation.go b/activation/activation.go index 13e1e25946..49fb0399d3 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -75,7 +75,6 @@ type Builder struct { localDB *localsql.Database publisher pubsub.Publisher nipostBuilder nipostBuilder - validator nipostValidator layerClock layerClock syncer syncer log *zap.Logger @@ -116,12 +115,6 @@ func WithPoetConfig(c PoetConfig) BuilderOption { } } -func WithValidator(v nipostValidator) BuilderOption { - return func(b *Builder) { - b.validator = v - } -} - // NewBuilder returns an atx builder that will start a routine that will attempt to create an atx upon each new layer. func NewBuilder( conf Config, diff --git a/activation/activation_multi_test.go b/activation/activation_multi_test.go index b9ac03f158..4c1e703731 100644 --- a/activation/activation_multi_test.go +++ b/activation/activation_multi_test.go @@ -231,10 +231,6 @@ func Test_Builder_Multi_InitialPost(t *testing.T) { }, nil, ) - tab.mValidator.EXPECT(). - Post(gomock.Any(), sig.NodeID(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - AnyTimes(). - Return(nil) require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) diff --git a/activation/activation_test.go b/activation/activation_test.go index 3f54e502a7..74599d57ac 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -105,7 +105,6 @@ type testAtxBuilder struct { mpostClient *MockPostClient mclock *MocklayerClock msync *Mocksyncer - mValidator *MocknipostValidator } func newTestBuilder(tb testing.TB, numSigners int, opts ...BuilderOption) *testAtxBuilder { @@ -123,11 +122,8 @@ func newTestBuilder(tb testing.TB, numSigners int, opts ...BuilderOption) *testA mpostClient: NewMockPostClient(ctrl), mclock: NewMocklayerClock(ctrl), msync: NewMocksyncer(ctrl), - mValidator: NewMocknipostValidator(ctrl), } - opts = append(opts, WithValidator(tab.mValidator)) - cfg := Config{ GoldenATXID: tab.goldenATXID, LayersPerEpoch: layersPerEpoch, @@ -1335,10 +1331,6 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { }, nil, ) - tab.mValidator.EXPECT(). - Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - AnyTimes(). - Return(nil) require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) posEpoch := postGenesisEpoch + 1 @@ -1388,10 +1380,6 @@ func TestBuilder_InitialPostIsPersisted(t *testing.T) { }, nil, ) - tab.mValidator.EXPECT(). - Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - AnyTimes(). - Return(nil) require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID())) // postClient.Proof() should not be called again diff --git a/activation/e2e/activation_test.go b/activation/e2e/activation_test.go new file mode 100644 index 0000000000..67ad4afbeb --- /dev/null +++ b/activation/e2e/activation_test.go @@ -0,0 +1,206 @@ +package activation_test + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/spacemeshos/post/initialization" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "go.uber.org/zap/zaptest" + "golang.org/x/sync/errgroup" + + "github.com/spacemeshos/go-spacemesh/activation" + "github.com/spacemeshos/go-spacemesh/api/grpcserver" + "github.com/spacemeshos/go-spacemesh/codec" + "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/datastore" + "github.com/spacemeshos/go-spacemesh/log" + "github.com/spacemeshos/go-spacemesh/p2p/pubsub" + "github.com/spacemeshos/go-spacemesh/p2p/pubsub/mocks" + "github.com/spacemeshos/go-spacemesh/signing" + "github.com/spacemeshos/go-spacemesh/sql" + "github.com/spacemeshos/go-spacemesh/sql/localsql" + "github.com/spacemeshos/go-spacemesh/timesync" +) + +func Test_BuilderWithMultipleClients(t *testing.T) { + ctrl := gomock.NewController(t) + + numSigners := 3 + signers := make(map[types.NodeID]*signing.EdSigner, numSigners) + for i := 0; i < numSigners; i++ { + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + signers[sig.NodeID()] = sig + } + + logger := zaptest.NewLogger(t) + goldenATX := types.ATXID{2, 3, 4} + cfg := activation.DefaultPostConfig() + db := sql.InMemory() + cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) + + syncer := activation.NewMocksyncer(gomock.NewController(t)) + syncer.EXPECT().RegisterForATXSynced().DoAndReturn(func() <-chan struct{} { + synced := make(chan struct{}) + close(synced) + return synced + }).AnyTimes() + + svc := grpcserver.NewPostService(logger) + grpcCfg, cleanup := launchServer(t, svc) + t.Cleanup(cleanup) + + opts := activation.DefaultPostSetupOpts() + opts.ProviderID.SetUint32(initialization.CPUProviderID()) + opts.Scrypt.N = 2 // Speedup initialization in tests. + + var eg errgroup.Group + for _, sig := range signers { + sig := sig + opts := opts + eg.Go(func() error { + mgr, err := activation.NewPostSetupManager(sig.NodeID(), cfg, logger, cdb, goldenATX, syncer) + require.NoError(t, err) + + opts.DataDir = t.TempDir() + initPost(t, mgr, opts) + t.Cleanup(launchPostSupervisor(t, logger, mgr, grpcCfg, opts)) + + require.Eventually(t, func() bool { + _, err := svc.Client(sig.NodeID()) + return err == nil + }, 10*time.Second, 100*time.Millisecond, "timed out waiting for connection") + return nil + }) + } + require.NoError(t, eg.Wait()) + + // ensure that genesis aligns with layer timings + genesis := time.Now().Add(layerDuration).Round(layerDuration) + layerDuration := 3 * time.Second + epoch := layersPerEpoch * layerDuration + poetCfg := activation.PoetConfig{ + PhaseShift: epoch / 2, + CycleGap: epoch / 4, + GracePeriod: epoch / 5, + RequestTimeout: epoch / 5, + RequestRetryDelay: epoch / 50, + MaxRequestRetries: 10, + } + poetProver := spawnPoet( + t, + WithGenesis(genesis), + WithEpochDuration(epoch), + WithPhaseShift(poetCfg.PhaseShift), + WithCycleGap(poetCfg.CycleGap), + ) + + clock, err := timesync.NewClock( + timesync.WithGenesisTime(genesis), + timesync.WithLayerDuration(layerDuration), + timesync.WithTickInterval(100*time.Millisecond), + timesync.WithLogger(logger), + ) + require.NoError(t, err) + + poetDb := activation.NewPoetDb(db, log.NewFromLog(logger).Named("poetDb")) + + localDB := localsql.InMemory() + nb, err := activation.NewNIPostBuilder( + localDB, + poetDb, + svc, + []types.PoetServer{{Address: poetProver.RestURL().String()}}, + logger.Named("nipostBuilder"), + poetCfg, + clock, + ) + require.NoError(t, err) + + conf := activation.Config{ + GoldenATXID: goldenATX, + LayersPerEpoch: layersPerEpoch, + RegossipInterval: 0, + } + + var atxMtx sync.Mutex + atxs := make(map[types.NodeID]types.ActivationTx) + endChan := make(chan struct{}) + mpub := mocks.NewMockPublisher(ctrl) + mpub.EXPECT().Publish(gomock.Any(), pubsub.AtxProtocol, gomock.Any()).DoAndReturn( + func(ctx context.Context, topic string, got []byte) error { + atxMtx.Lock() + defer atxMtx.Unlock() + var gotAtx types.ActivationTx + require.NoError(t, codec.Decode(got, &gotAtx)) + atxs[gotAtx.SmesherID] = gotAtx + if len(atxs) == numSigners { + close(endChan) + } + return nil + }, + ).Times(numSigners) + + tab := activation.NewBuilder( + conf, + cdb, + localDB, + mpub, + nb, + clock, + syncer, + logger, + activation.WithPoetConfig(poetCfg), + ) + for _, sig := range signers { + tab.Register(sig) + } + + require.NoError(t, tab.StartSmeshing(types.Address{})) + + <-endChan + + require.NoError(t, tab.StopSmeshing(false)) + + verifier, err := activation.NewPostVerifier(cfg, logger.Named("verifier")) + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) + + v := activation.NewValidator(poetDb, cfg, opts.Scrypt, verifier) + for _, sig := range signers { + atx := atxs[sig.NodeID()] + + _, err = v.NIPost( + context.Background(), + sig.NodeID(), + *atx.CommitmentATX, + atx.NIPost, + atx.NIPostChallenge.Hash(), + atx.NumUnits, + ) + require.NoError(t, err) + + err := v.VRFNonce( + sig.NodeID(), + *atx.CommitmentATX, + atx.VRFNonce, + atx.NIPost.PostMetadata, + atx.NumUnits, + ) + require.NoError(t, err) + + require.Equal(t, postGenesisEpoch, atx.NIPostChallenge.TargetEpoch()) + require.Equal(t, types.EmptyATXID, atx.NIPostChallenge.PrevATXID) + require.Equal(t, goldenATX, atx.NIPostChallenge.PositioningATX) + require.Equal(t, uint64(0), atx.NIPostChallenge.Sequence) + + require.Equal(t, types.Address{}, atx.Coinbase) + require.Equal(t, sig.NodeID(), *atx.NodeID) + } +} diff --git a/activation/e2e/nipost_test.go b/activation/e2e/nipost_test.go index b9c778d10e..a9d45e91eb 100644 --- a/activation/e2e/nipost_test.go +++ b/activation/e2e/nipost_test.go @@ -117,7 +117,8 @@ func TestNIPostBuilderWithClients(t *testing.T) { logger := zaptest.NewLogger(t) goldenATX := types.ATXID{2, 3, 4} cfg := activation.DefaultPostConfig() - cdb := datastore.NewCachedDB(sql.InMemory(), log.NewFromLog(logger)) + db := sql.InMemory() + cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) syncer := activation.NewMocksyncer(gomock.NewController(t)) syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { @@ -165,7 +166,7 @@ func TestNIPostBuilderWithClients(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) - poetDb := activation.NewPoetDb(sql.InMemory(), log.NewFromLog(logger).Named("poetDb")) + poetDb := activation.NewPoetDb(db, log.NewFromLog(logger).Named("poetDb")) svc := grpcserver.NewPostService(logger) grpcCfg, cleanup := launchServer(t, svc) @@ -178,9 +179,9 @@ func TestNIPostBuilderWithClients(t *testing.T) { return err == nil }, 10*time.Second, 100*time.Millisecond, "timed out waiting for connection") - db := localsql.InMemory() + localDB := localsql.InMemory() nb, err := activation.NewNIPostBuilder( - db, + localDB, poetDb, svc, []types.PoetServer{{Address: poetProver.RestURL().String()}}, @@ -261,7 +262,8 @@ func TestNewNIPostBuilderNotInitialized(t *testing.T) { logger := zaptest.NewLogger(t) goldenATX := types.ATXID{2, 3, 4} cfg := activation.DefaultPostConfig() - cdb := datastore.NewCachedDB(sql.InMemory(), log.NewFromLog(logger)) + db := sql.InMemory() + cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) syncer := activation.NewMocksyncer(gomock.NewController(t)) syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { @@ -299,15 +301,15 @@ func TestNewNIPostBuilderNotInitialized(t *testing.T) { }, ) - poetDb := activation.NewPoetDb(sql.InMemory(), log.NewFromLog(logger).Named("poetDb")) + poetDb := activation.NewPoetDb(db, log.NewFromLog(logger).Named("poetDb")) svc := grpcserver.NewPostService(logger) grpcCfg, cleanup := launchServer(t, svc) t.Cleanup(cleanup) - db := localsql.InMemory() + localDB := localsql.InMemory() nb, err := activation.NewNIPostBuilder( - db, + localDB, poetDb, svc, []types.PoetServer{{Address: poetProver.RestURL().String()}}, @@ -365,7 +367,8 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { logger := zaptest.NewLogger(t) goldenATX := types.ATXID{2, 3, 4} cfg := activation.DefaultPostConfig() - cdb := datastore.NewCachedDB(sql.InMemory(), log.NewFromLog(logger)) + db := sql.InMemory() + cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) syncer := activation.NewMocksyncer(gomock.NewController(t)) syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { @@ -429,15 +432,11 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { }, ) - verifier, err := activation.NewPostVerifier(cfg, logger.Named("verifier")) - require.NoError(t, err) - t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) - - poetDb := activation.NewPoetDb(sql.InMemory(), log.NewFromLog(logger).Named("poetDb")) + poetDb := activation.NewPoetDb(db, log.NewFromLog(logger).Named("poetDb")) - db := localsql.InMemory() + localDB := localsql.InMemory() nb, err := activation.NewNIPostBuilder( - db, + localDB, poetDb, svc, []types.PoetServer{{Address: poetProver.RestURL().String()}}, @@ -447,10 +446,13 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { ) require.NoError(t, err) + verifier, err := activation.NewPostVerifier(cfg, logger.Named("verifier")) + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) + challenge := types.NIPostChallenge{ PublishEpoch: postGenesisEpoch + 2, } - for _, sig := range signers { sig := sig eg.Go(func() error { diff --git a/node/node.go b/node/node.go index bf7b2dd7df..a1333926f3 100644 --- a/node/node.go +++ b/node/node.go @@ -972,7 +972,6 @@ func (app *App) initServices(ctx context.Context) error { activation.WithPoetConfig(app.Config.POET), // TODO(dshulyak) makes no sense. how we ended using it? activation.WithPoetRetryInterval(app.Config.HARE3.PreroundDelay), - activation.WithValidator(app.validator), ) atxBuilder.Register(app.edSgn) @@ -1830,7 +1829,7 @@ func (app *App) startSynchronous(ctx context.Context) (err error) { timesync.WithLayerDuration(app.Config.LayerDuration), timesync.WithTickInterval(1*time.Second), timesync.WithGenesisTime(gTime), - timesync.WithLogger(app.addLogger(ClockLogger, lg)), + timesync.WithLogger(app.addLogger(ClockLogger, lg).Zap()), ) if err != nil { return fmt.Errorf("cannot create clock: %w", err) diff --git a/timesync/clock.go b/timesync/clock.go index 5ce9325666..a42fd86a23 100644 --- a/timesync/clock.go +++ b/timesync/clock.go @@ -6,10 +6,10 @@ import ( "github.com/jonboulle/clockwork" "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/log" "github.com/spacemeshos/go-spacemesh/metrics" ) @@ -37,7 +37,7 @@ type NodeClock struct { stop chan struct{} once sync.Once - log log.Log + log *zap.Logger eg errgroup.Group } @@ -55,9 +55,9 @@ func NewClock(opts ...OptionFunc) (*NodeClock, error) { } gtime := cfg.genesisTime.Local() - cfg.log.With().Info("converting genesis time to local time", - log.Time("genesis", cfg.genesisTime), - log.Time("local", gtime), + cfg.log.Info("converting genesis time to local time", + zap.Time("genesis", cfg.genesisTime), + zap.Time("local", gtime), ) t := &NodeClock{ LayerConverter: LayerConverter{duration: cfg.layerDuration, genesis: gtime}, @@ -66,7 +66,7 @@ func NewClock(opts ...OptionFunc) (*NodeClock, error) { layerChannels: make(map[types.LayerID]chan struct{}), genesis: gtime, stop: make(chan struct{}), - log: *cfg.log, + log: cfg.log, } t.eg.Go(t.startClock) @@ -74,18 +74,18 @@ func NewClock(opts ...OptionFunc) (*NodeClock, error) { } func (t *NodeClock) startClock() error { - t.log.With().Info("starting global clock", - log.Time("now", t.clock.Now()), - log.Time("genesis", t.genesis), - log.Duration("layer_duration", t.duration), - log.Duration("tick_interval", t.tickInterval), + t.log.Info("starting global clock", + zap.Time("now", t.clock.Now()), + zap.Time("genesis", t.genesis), + zap.Duration("layer_duration", t.duration), + zap.Duration("tick_interval", t.tickInterval), ) ticker := t.clock.NewTicker(t.tickInterval) for { currLayer := t.TimeToLayer(t.clock.Now()) - t.log.With().Debug("global clock going to sleep before next tick", - log.Stringer("curr_layer", currLayer), + t.log.Debug("global clock going to sleep before next tick", + zap.Stringer("curr_layer", currLayer), ) select { @@ -111,7 +111,7 @@ func (t *NodeClock) Close() { t.log.Info("stopping clock") close(t.stop) if err := t.eg.Wait(); err != nil { - t.log.Error("errgroup: %v", err) + t.log.Error("failed to stop clock", zap.Error(err)) } t.log.Info("clock stopped") }) @@ -130,17 +130,17 @@ func (t *NodeClock) tick() { layer := t.TimeToLayer(t.clock.Now()) switch { case layer.Before(t.lastTicked): - t.log.With().Info("clock ticked back in time", - log.Stringer("layer", layer), - log.Stringer("last_ticked_layer", t.lastTicked), + t.log.Info("clock ticked back in time", + zap.Stringer("layer", layer), + zap.Stringer("last_ticked_layer", t.lastTicked), ) d := t.lastTicked.Difference(layer) tickDistance.Observe(float64(-d)) // don't warn right after fresh startup case layer.Difference(t.lastTicked) > 1 && t.lastTicked > 0: - t.log.With().Warning("clock skipped layers", - log.Stringer("layer", layer), - log.Stringer("last_ticked_layer", t.lastTicked), + t.log.Warn("clock skipped layers", + zap.Stringer("layer", layer), + zap.Stringer("last_ticked_layer", t.lastTicked), ) d := layer.Difference(t.lastTicked) tickDistance.Observe(float64(d)) diff --git a/timesync/clock_options.go b/timesync/clock_options.go index 442d75837f..8b15fc1629 100644 --- a/timesync/clock_options.go +++ b/timesync/clock_options.go @@ -5,8 +5,7 @@ import ( "time" "github.com/jonboulle/clockwork" - - "github.com/spacemeshos/go-spacemesh/log" + "go.uber.org/zap" ) type option struct { @@ -15,7 +14,7 @@ type option struct { layerDuration time.Duration tickInterval time.Duration - log *log.Log + log *zap.Logger } func (o *option) validate() error { @@ -75,9 +74,9 @@ func WithTickInterval(d time.Duration) OptionFunc { } // WithLogger sets the logger for the NodeClock. -func WithLogger(logger log.Log) OptionFunc { +func WithLogger(logger *zap.Logger) OptionFunc { return func(opts *option) error { - opts.log = &logger + opts.log = logger return nil } } diff --git a/timesync/clock_test.go b/timesync/clock_test.go index 889bd7ac3a..b16c40c685 100644 --- a/timesync/clock_test.go +++ b/timesync/clock_test.go @@ -9,17 +9,17 @@ import ( "github.com/jonboulle/clockwork" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/log/logtest" ) func Test_NodeClock_NewClock(t *testing.T) { clock, err := NewClock( WithGenesisTime(time.Now()), WithTickInterval(time.Second), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.ErrorContains(t, err, "layer duration is zero") require.Nil(t, clock) @@ -27,7 +27,7 @@ func Test_NodeClock_NewClock(t *testing.T) { clock, err = NewClock( WithLayerDuration(time.Second), WithTickInterval(time.Second), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.ErrorContains(t, err, "genesis time is zero") require.Nil(t, clock) @@ -43,7 +43,7 @@ func Test_NodeClock_NewClock(t *testing.T) { clock, err = NewClock( WithLayerDuration(time.Second), WithGenesisTime(time.Now()), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.ErrorContains(t, err, "tick interval is zero") require.Nil(t, clock) @@ -52,7 +52,7 @@ func Test_NodeClock_NewClock(t *testing.T) { WithLayerDuration(time.Second), WithTickInterval(2*time.Second), WithGenesisTime(time.Now()), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.ErrorContains(t, err, "tick interval must be between 0 and layer duration") require.Nil(t, clock) @@ -65,7 +65,7 @@ func Test_NodeClock_GenesisTime(t *testing.T) { WithLayerDuration(time.Second), WithTickInterval(time.Second/10), WithGenesisTime(genesis), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) @@ -78,7 +78,7 @@ func Test_NodeClock_Close(t *testing.T) { WithLayerDuration(time.Second), WithTickInterval(time.Second/10), WithGenesisTime(time.Now()), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) @@ -105,7 +105,7 @@ func Test_NodeClock_NoRaceOnTick(t *testing.T) { WithLayerDuration(time.Second), WithTickInterval(time.Second/10), WithGenesisTime(time.Now()), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) @@ -148,7 +148,7 @@ func Test_NodeClock_Await_BeforeGenesis(t *testing.T) { WithLayerDuration(layerDuration), WithTickInterval(tickInterval), WithGenesisTime(genesis), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) @@ -173,7 +173,7 @@ func Test_NodeClock_Await_PassedLayer(t *testing.T) { WithLayerDuration(layerDuration), WithTickInterval(tickInterval), WithGenesisTime(genesis), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) @@ -199,7 +199,7 @@ func Test_NodeClock_Await_WithClockMovingBackwards(t *testing.T) { WithLayerDuration(layerDuration), WithTickInterval(tickInterval), WithGenesisTime(genesis), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) @@ -248,7 +248,7 @@ func Test_NodeClock_NonMonotonicTick_Forward(t *testing.T) { WithLayerDuration(layerDuration), WithTickInterval(tickInterval), WithGenesisTime(genesis), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) @@ -286,7 +286,7 @@ func Test_NodeClock_NonMonotonicTick_Backward(t *testing.T) { WithLayerDuration(layerDuration), WithTickInterval(tickInterval), WithGenesisTime(genesis), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) @@ -382,7 +382,7 @@ func Fuzz_NodeClock_CurrentLayer(f *testing.F) { WithLayerDuration(layerTime), WithTickInterval(tickInterval), WithGenesisTime(genesisTime), - WithLogger(logtest.New(t)), + WithLogger(zaptest.NewLogger(t)), ) require.NoError(t, err) require.NotNil(t, clock) From e06ab214323a8f0fed3763ec34075a736ac3ad11 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:19:24 +0000 Subject: [PATCH 24/26] Fix issues after merge --- activation/activation_test.go | 4 +++- activation/e2e/activation_test.go | 8 ++++---- activation/e2e/nipost_test.go | 8 ++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/activation/activation_test.go b/activation/activation_test.go index f9cae174dd..81eb23a4ab 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -128,6 +128,8 @@ func newTestBuilder(tb testing.TB, numSigners int, opts ...BuilderOption) *testA mValidator: NewMocknipostValidator(ctrl), } + opts = append(opts, WithValidator(tab.mValidator)) + cfg := Config{ GoldenATXID: tab.goldenATXID, } @@ -1532,7 +1534,7 @@ func TestGetPositioningAtxDbFailed(t *testing.T) { tab := newTestBuilder(t, 1) sig := maps.Values(tab.signers)[0] - db := datastoremocks.NewMockExecutor(gomock.NewController(t)) + db := datastoremocks.NewMockExecutor(tab.mctrl) tab.Builder.cdb = datastore.NewCachedDB(db, logtest.New(t)) expected := errors.New("db error") db.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Any()).Return(0, expected) diff --git a/activation/e2e/activation_test.go b/activation/e2e/activation_test.go index 67ad4afbeb..6ae197190e 100644 --- a/activation/e2e/activation_test.go +++ b/activation/e2e/activation_test.go @@ -45,7 +45,7 @@ func Test_BuilderWithMultipleClients(t *testing.T) { db := sql.InMemory() cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) - syncer := activation.NewMocksyncer(gomock.NewController(t)) + syncer := activation.NewMocksyncer(ctrl) syncer.EXPECT().RegisterForATXSynced().DoAndReturn(func() <-chan struct{} { synced := make(chan struct{}) close(synced) @@ -65,7 +65,8 @@ func Test_BuilderWithMultipleClients(t *testing.T) { sig := sig opts := opts eg.Go(func() error { - mgr, err := activation.NewPostSetupManager(sig.NodeID(), cfg, logger, cdb, goldenATX, syncer) + validator := activation.NewMocknipostValidator(ctrl) + mgr, err := activation.NewPostSetupManager(sig.NodeID(), cfg, logger, cdb, goldenATX, syncer, validator) require.NoError(t, err) opts.DataDir = t.TempDir() @@ -125,7 +126,6 @@ func Test_BuilderWithMultipleClients(t *testing.T) { conf := activation.Config{ GoldenATXID: goldenATX, - LayersPerEpoch: layersPerEpoch, RegossipInterval: 0, } @@ -172,7 +172,7 @@ func Test_BuilderWithMultipleClients(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) - v := activation.NewValidator(poetDb, cfg, opts.Scrypt, verifier) + v := activation.NewValidator(nil, poetDb, cfg, opts.Scrypt, verifier) for _, sig := range signers { atx := atxs[sig.NodeID()] diff --git a/activation/e2e/nipost_test.go b/activation/e2e/nipost_test.go index 3642b5de15..b284ff6bc6 100644 --- a/activation/e2e/nipost_test.go +++ b/activation/e2e/nipost_test.go @@ -120,7 +120,7 @@ func TestNIPostBuilderWithClients(t *testing.T) { db := sql.InMemory() cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) - syncer := activation.NewMocksyncer(gomock.NewController(t)) + syncer := activation.NewMocksyncer(ctrl) syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { synced := make(chan struct{}) close(synced) @@ -266,14 +266,14 @@ func TestNewNIPostBuilderNotInitialized(t *testing.T) { db := sql.InMemory() cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) - syncer := activation.NewMocksyncer(gomock.NewController(t)) + syncer := activation.NewMocksyncer(ctrl) syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { synced := make(chan struct{}) close(synced) return synced }) - validator := activation.NewMocknipostValidator(gomock.NewController(t)) + validator := activation.NewMocknipostValidator(ctrl) mgr, err := activation.NewPostSetupManager(sig.NodeID(), cfg, logger, cdb, goldenATX, syncer, validator) require.NoError(t, err) @@ -372,7 +372,7 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { db := sql.InMemory() cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) - syncer := activation.NewMocksyncer(gomock.NewController(t)) + syncer := activation.NewMocksyncer(ctrl) syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { synced := make(chan struct{}) close(synced) From 8dfbcb370f9ebd5b35dadfcfd9ff87fb885570d4 Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:37:36 +0000 Subject: [PATCH 25/26] Cleanup and test fixes --- activation/activation.go | 4 ++-- activation/activation_test.go | 4 ++-- activation/e2e/activation_test.go | 1 + activation/e2e/nipost_test.go | 10 +++++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index 1a88d517f8..67044a1968 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -438,7 +438,7 @@ func (b *Builder) buildNIPostChallenge(ctx context.Context, nodeID types.NodeID) } } - posAtx, err := b.GetPositioningAtx(ctx, nodeID) + posAtx, err := b.getPositioningAtx(ctx, nodeID) if err != nil { return nil, fmt.Errorf("failed to get positioning ATX: %w", err) } @@ -625,7 +625,7 @@ func (b *Builder) broadcast(ctx context.Context, atx *types.ActivationTx) (int, } // getPositioningAtx returns atx id with the highest tick height. -func (b *Builder) GetPositioningAtx(ctx context.Context, nodeID types.NodeID) (types.ATXID, error) { +func (b *Builder) getPositioningAtx(ctx context.Context, nodeID types.NodeID) (types.ATXID, error) { id, err := findFullyValidHighTickAtx( ctx, b.cdb, diff --git a/activation/activation_test.go b/activation/activation_test.go index 81eb23a4ab..20f182a239 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -1525,7 +1525,7 @@ func TestGetPositioningAtxPicksAtxWithValidChain(t *testing.T) { tab.mValidator.EXPECT(). VerifyChain(gomock.Any(), validAtx.ID(), tab.goldenATXID, gomock.Any()) - posAtxID, err := tab.GetPositioningAtx(context.Background(), sig.NodeID()) + posAtxID, err := tab.getPositioningAtx(context.Background(), sig.NodeID()) require.NoError(t, err) require.Equal(t, posAtxID, vValidAtx.ID()) } @@ -1539,7 +1539,7 @@ func TestGetPositioningAtxDbFailed(t *testing.T) { expected := errors.New("db error") db.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Any()).Return(0, expected) - none, err := tab.GetPositioningAtx(context.Background(), sig.NodeID()) + none, err := tab.getPositioningAtx(context.Background(), sig.NodeID()) require.ErrorIs(t, err, expected) require.Equal(t, types.ATXID{}, none) } diff --git a/activation/e2e/activation_test.go b/activation/e2e/activation_test.go index 6ae197190e..d27b25ca57 100644 --- a/activation/e2e/activation_test.go +++ b/activation/e2e/activation_test.go @@ -109,6 +109,7 @@ func Test_BuilderWithMultipleClients(t *testing.T) { timesync.WithLogger(logger), ) require.NoError(t, err) + t.Cleanup(clock.Close) poetDb := activation.NewPoetDb(db, log.NewFromLog(logger).Named("poetDb")) diff --git a/activation/e2e/nipost_test.go b/activation/e2e/nipost_test.go index b284ff6bc6..1ba62cef65 100644 --- a/activation/e2e/nipost_test.go +++ b/activation/e2e/nipost_test.go @@ -387,12 +387,12 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { opts.ProviderID.SetUint32(initialization.CPUProviderID()) opts.Scrypt.N = 2 // Speedup initialization in tests. + validator := activation.NewMocknipostValidator(ctrl) var eg errgroup.Group for _, sig := range signers { sig := sig opts := opts eg.Go(func() error { - validator := activation.NewMocknipostValidator(ctrl) mgr, err := activation.NewPostSetupManager(sig.NodeID(), cfg, logger, cdb, goldenATX, syncer, validator) require.NoError(t, err) @@ -435,6 +435,10 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { }, ) + verifier, err := activation.NewPostVerifier(cfg, logger.Named("verifier")) + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) + poetDb := activation.NewPoetDb(db, log.NewFromLog(logger).Named("poetDb")) localDB := localsql.InMemory() @@ -449,10 +453,6 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { ) require.NoError(t, err) - verifier, err := activation.NewPostVerifier(cfg, logger.Named("verifier")) - require.NoError(t, err) - t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) - challenge := types.NIPostChallenge{ PublishEpoch: postGenesisEpoch + 2, } From 512cd65d691b7dee0017010429f5809fb8828efb Mon Sep 17 00:00:00 2001 From: Matthias <5011972+fasmat@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:58:39 +0000 Subject: [PATCH 26/26] Fix races --- systest/tests/distributed_post_verification_test.go | 1 + timesync/clock_test.go | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/systest/tests/distributed_post_verification_test.go b/systest/tests/distributed_post_verification_test.go index a9ebc11db3..9805c976a8 100644 --- a/systest/tests/distributed_post_verification_test.go +++ b/systest/tests/distributed_post_verification_test.go @@ -146,6 +146,7 @@ func TestPostMalfeasanceProof(t *testing.T) { timesync.WithLogger(logger.Named("clock")), ) require.NoError(t, err) + t.Cleanup(clock.Close) grpcPostService := grpcserver.NewPostService(logger.Named("grpc-post-service")) grpczap.SetGrpcLoggerV2(grpclog, logger.Named("grpc")) diff --git a/timesync/clock_test.go b/timesync/clock_test.go index b16c40c685..3799446ed8 100644 --- a/timesync/clock_test.go +++ b/timesync/clock_test.go @@ -69,6 +69,7 @@ func Test_NodeClock_GenesisTime(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) require.Equal(t, genesis.Local(), clock.GenesisTime()) } @@ -82,6 +83,7 @@ func Test_NodeClock_Close(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) var eg errgroup.Group eg.Go(func() error { @@ -109,6 +111,7 @@ func Test_NodeClock_NoRaceOnTick(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) ctx, cancel := context.WithCancel(context.Background()) eg, egCtx := errgroup.WithContext(ctx) @@ -152,6 +155,7 @@ func Test_NodeClock_Await_BeforeGenesis(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) select { case <-clock.AwaitLayer(types.LayerID(0)): @@ -177,6 +181,7 @@ func Test_NodeClock_Await_PassedLayer(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) select { case <-clock.AwaitLayer(types.LayerID(4)): @@ -203,6 +208,7 @@ func Test_NodeClock_Await_WithClockMovingBackwards(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) // make the clock tick clock.tick() @@ -252,6 +258,7 @@ func Test_NodeClock_NonMonotonicTick_Forward(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) ch := clock.AwaitLayer(types.LayerID(6)) select { @@ -290,6 +297,7 @@ func Test_NodeClock_NonMonotonicTick_Backward(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) ch6 := clock.AwaitLayer(types.LayerID(6)) ch7 := clock.AwaitLayer(types.LayerID(7)) @@ -386,6 +394,7 @@ func Fuzz_NodeClock_CurrentLayer(f *testing.F) { ) require.NoError(t, err) require.NotNil(t, clock) + t.Cleanup(clock.Close) expectedLayer := uint32(0) if nowTime.After(genesisTime) {