diff --git a/activation/activation.go b/activation/activation.go index fbeb90c403..f407a9b322 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -16,6 +16,7 @@ import ( "github.com/spacemeshos/go-spacemesh/activation/metrics" "github.com/spacemeshos/go-spacemesh/activation/wire" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/events" @@ -71,6 +72,7 @@ type Builder struct { coinbaseAccount types.Address conf Config db sql.Executor + atxsdata *atxsdata.Data localDB *localsql.Database publisher pubsub.Publisher nipostBuilder nipostBuilder @@ -151,6 +153,7 @@ func WithPostStates(ps PostStates) BuilderOption { func NewBuilder( conf Config, db sql.Executor, + atxsdata *atxsdata.Data, localDB *localsql.Database, publisher pubsub.Publisher, nipostBuilder nipostBuilder, @@ -164,6 +167,7 @@ func NewBuilder( signers: make(map[types.NodeID]*signing.EdSigner), conf: conf, db: db, + atxsdata: atxsdata, localDB: localDB, publisher: publisher, nipostBuilder: nipostBuilder, @@ -506,12 +510,6 @@ func (b *Builder) BuildNIPostChallenge(ctx context.Context, nodeID types.NodeID) } } - posAtx, err := b.getPositioningAtx(ctx, nodeID, publish) - if err != nil { - return nil, fmt.Errorf("failed to get positioning ATX: %w", err) - } - logger.Info("selected positioning atx", log.ZShortStringer("atx_id", posAtx)) - prevAtx, err = b.GetPrevAtx(nodeID) switch { case errors.Is(err, sql.ErrNotFound): @@ -537,6 +535,10 @@ func (b *Builder) BuildNIPostChallenge(ctx context.Context, nodeID types.NodeID) } return nil, fmt.Errorf("initial POST is invalid: %w", err) } + posAtx, err := b.getPositioningAtx(ctx, nodeID, publish, nil) + if err != nil { + return nil, fmt.Errorf("failed to get positioning ATX: %w", err) + } challenge = &types.NIPostChallenge{ PublishEpoch: publish, Sequence: 0, @@ -553,6 +555,10 @@ func (b *Builder) BuildNIPostChallenge(ctx context.Context, nodeID types.NodeID) return nil, fmt.Errorf("get last ATX: %w", err) default: // regular ATX challenge + posAtx, err := b.getPositioningAtx(ctx, nodeID, publish, prevAtx) + if err != nil { + return nil, fmt.Errorf("failed to get positioning ATX: %w", err) + } challenge = &types.NIPostChallenge{ PublishEpoch: publish, Sequence: prevAtx.Sequence + 1, @@ -693,6 +699,12 @@ func (b *Builder) createAtx( break } if nipostState.VRFNonce != oldNonce { + b.log.Info( + "attaching a new VRF nonce in ATX", + log.ZShortStringer("smesherID", sig.NodeID()), + zap.Uint64("new nonce", uint64(nipostState.VRFNonce)), + zap.Uint64("old nonce", uint64(oldNonce)), + ) nonce = &nipostState.VRFNonce } } @@ -724,9 +736,9 @@ func (b *Builder) broadcast(ctx context.Context, atx *types.ActivationTx) (int, return len(buf), nil } -// getPositioningAtx returns atx id with the highest tick height. +// searchPositioningAtx returns atx id with the highest tick height. // publish epoch is used for caching the positioning atx. -func (b *Builder) getPositioningAtx( +func (b *Builder) searchPositioningAtx( ctx context.Context, nodeID types.NodeID, publish types.EpochID, @@ -739,11 +751,17 @@ func (b *Builder) getPositioningAtx( return found.id, nil } - logger.Info("searching for positioning atx") + latestPublished, err := atxs.LatestEpoch(b.db) + if err != nil { + return types.EmptyATXID, fmt.Errorf("get latest epoch: %w", err) + } + logger.Info("searching for positioning atx", zap.Uint32("latest_epoch", latestPublished.Uint32())) + // positioning ATX publish epoch must be lower than the publish epoch of built ATX + positioningAtxPublished := min(latestPublished, publish-1) id, err := findFullyValidHighTickAtx( ctx, - b.db, - nodeID, + b.atxsdata, + positioningAtxPublished, b.conf.GoldenATXID, b.validator, logger, @@ -751,22 +769,47 @@ func (b *Builder) getPositioningAtx( VerifyChainOpts.WithTrustedID(nodeID), VerifyChainOpts.WithLogger(b.log), ) - switch { - case err == nil: - b.posAtxFinder.found = &struct { - id types.ATXID - forPublish types.EpochID - }{id, publish} - return id, nil - case errors.Is(err, sql.ErrNotFound): - logger.Info("using golden atx as positioning atx") - b.posAtxFinder.found = &struct { - id types.ATXID - forPublish types.EpochID - }{b.conf.GoldenATXID, publish} - return b.conf.GoldenATXID, nil + if err != nil { + logger.Info("search failed - using golden atx as positioning atx", zap.Error(err)) + id = b.conf.GoldenATXID + } + b.posAtxFinder.found = &struct { + id types.ATXID + forPublish types.EpochID + }{id, publish} + + return id, nil +} + +// getPositioningAtx returns the positioning ATX. +// The provided previous ATX is picked if it has a greater or equal +// tick count as the ATX selected in `searchPositioningAtx`. +func (b *Builder) getPositioningAtx( + ctx context.Context, + nodeID types.NodeID, + publish types.EpochID, + previous *types.VerifiedActivationTx, +) (types.ATXID, error) { + id, err := b.searchPositioningAtx(ctx, nodeID, publish) + if err != nil { + return types.EmptyATXID, err + } + + if previous != nil { + switch { + case id == b.conf.GoldenATXID: + id = previous.ID() + case id != b.conf.GoldenATXID: + if candidate, err := atxs.Get(b.db, id); err == nil { + if previous.TickHeight() >= candidate.TickHeight() { + id = previous.ID() + } + } + } } - return id, err + + b.log.Info("selected positioning atx", log.ZShortStringer("id", id), log.ZShortStringer("smesherID", nodeID)) + return id, nil } func (b *Builder) Regossip(ctx context.Context, nodeID types.NodeID) error { @@ -808,35 +851,26 @@ func buildNipostChallengeStartDeadline(roundStart time.Time, gracePeriod time.Du func findFullyValidHighTickAtx( ctx context.Context, - db sql.Executor, - prefNodeID types.NodeID, + atxdata *atxsdata.Data, + publish types.EpochID, goldenATXID types.ATXID, validator nipostValidator, logger *zap.Logger, opts ...VerifyChainOption, ) (types.ATXID, error) { - rejectedAtxs := make(map[types.ATXID]struct{}) - filter := func(id types.ATXID) bool { - _, ok := rejectedAtxs[id] - return !ok - } - - for { - select { - case <-ctx.Done(): - return types.ATXID{}, ctx.Err() - default: - } - id, err := atxs.GetIDWithMaxHeight(db, prefNodeID, filter) - if err != nil { - return types.ATXID{}, err - } - logger.Info("found candidate for high-tick atx, verifying its chain", log.ZShortStringer("atx_id", id)) + var found *types.ATXID + atxdata.IterateHighTicksInEpoch(publish+1, func(id types.ATXID) bool { + logger.Info("found candidate for high-tick atx", log.ZShortStringer("id", id)) if err := validator.VerifyChain(ctx, id, goldenATXID, opts...); err != nil { - logger.Info("rejecting candidate for high-tick atx", zap.Error(err), log.ZShortStringer("atx_id", id)) - rejectedAtxs[id] = struct{}{} - } else { - return id, nil + logger.Info("rejecting candidate for high-tick atx", zap.Error(err), log.ZShortStringer("id", id)) + return true } + found = &id + return false + }) + + if found != nil { + return *found, nil } + return types.ATXID{}, ErrNotFound } diff --git a/activation/activation_test.go b/activation/activation_test.go index dbbab151fd..f18903d547 100644 --- a/activation/activation_test.go +++ b/activation/activation_test.go @@ -16,11 +16,13 @@ import ( "go.uber.org/mock/gomock" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest/observer" "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/activation/wire" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/events" @@ -63,6 +65,14 @@ func newAtx( return atx } +func toVerified(t *testing.T, atx *types.ActivationTx, ticks uint64) *types.VerifiedActivationTx { + atx.SetReceived(time.Now()) + atx.SetEffectiveNumUnits(atx.NumUnits) + vatx, err := atx.Verify(0, ticks) + require.NoError(t, err) + return vatx +} + type atxOption func(*types.ActivationTx) func withVrfNonce(nonce types.VRFPostIndex) atxOption { @@ -157,6 +167,7 @@ func newTestBuilder(tb testing.TB, numSigners int, opts ...BuilderOption) *testA b := NewBuilder( cfg, tab.db, + atxsdata.New(), tab.localDb, tab.mpub, tab.mnipost, @@ -233,6 +244,7 @@ func publishAtx( vatx, err := built.Verify(0, 1) require.NoError(tb, err) require.NoError(tb, atxs.Add(tab.db, vatx)) + tab.atxsdata.AddFromHeader(vatx.ToHeader(), nonce, false) return nil }) @@ -406,12 +418,14 @@ func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPrevAtx)) + tab.atxsdata.AddFromHeader(vPrevAtx.ToHeader(), nipostData.VRFNonce, false) // create and publish ATX tab.mclock.EXPECT().CurrentLayer().Return(currLayer).Times(4) atx1, err := publishAtx(t, tab, sig.NodeID(), posEpoch, &currLayer, layersPerEpoch) require.NoError(t, err) require.NotNil(t, atx1) + require.Equal(t, prevAtx.ID(), atx1.PositioningATX) // create and publish another ATX currLayer = (posEpoch + 1).FirstLayer() @@ -421,7 +435,7 @@ func TestBuilder_PublishActivationTx_HappyFlow(t *testing.T) { require.NotNil(t, atx2) require.NotEqual(t, atx1, atx2) require.Equal(t, atx1.TargetEpoch()+1, atx2.TargetEpoch()) - + require.Equal(t, atx1.ID(), atx2.PositioningATX) // state is cleaned up _, err = nipost.Challenge(tab.localDB, sig.NodeID()) require.ErrorIs(t, err, sql.ErrNotFound) @@ -449,6 +463,7 @@ func TestBuilder_Loop_WaitsOnStaleChallenge(t *testing.T) { vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPrevAtx)) + tab.atxsdata.AddFromHeader(vPrevAtx.ToHeader(), nipostData.VRFNonce, false) tab.mclock.EXPECT().CurrentLayer().Return(currLayer).AnyTimes() tab.mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( @@ -507,6 +522,7 @@ func TestBuilder_PublishActivationTx_FaultyNet(t *testing.T) { vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPrevAtx)) + tab.atxsdata.AddFromHeader(vPrevAtx.ToHeader(), nipostData.VRFNonce, false) publishEpoch := posEpoch + 1 tab.mclock.EXPECT().CurrentLayer().DoAndReturn(func() types.LayerID { return currLayer }).AnyTimes() @@ -596,6 +612,7 @@ func TestBuilder_PublishActivationTx_UsesExistingChallengeOnLatePublish(t *testi vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPrevAtx)) + tab.atxsdata.AddFromHeader(vPrevAtx.ToHeader(), nipostData.VRFNonce, false) publishEpoch := currLayer.GetEpoch() tab.mclock.EXPECT().CurrentLayer().DoAndReturn(func() types.LayerID { return currLayer }).AnyTimes() @@ -681,6 +698,7 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPrevAtx)) + tab.atxsdata.AddFromHeader(vPrevAtx.ToHeader(), nipostData.VRFNonce, false) publishEpoch := posEpoch + 1 tab.mclock.EXPECT().CurrentLayer().DoAndReturn( @@ -752,6 +770,7 @@ func TestBuilder_PublishActivationTx_RebuildNIPostWhenTargetEpochPassed(t *testi vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPosAtx)) + tab.atxsdata.AddFromHeader(vPosAtx.ToHeader(), nipostData.VRFNonce, false) tab.mclock.EXPECT().CurrentLayer().DoAndReturn(func() types.LayerID { return currLayer }).AnyTimes() tab.mnipost.EXPECT().ResetState(sig.NodeID()).Return(nil) built2, err := publishAtx(t, tab, sig.NodeID(), posEpoch, &currLayer, layersPerEpoch) @@ -786,6 +805,7 @@ func TestBuilder_PublishActivationTx_NoPrevATX(t *testing.T) { vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPosAtx)) + tab.atxsdata.AddFromHeader(vPosAtx.ToHeader(), nipostData.VRFNonce, false) // generate and store initial post in state post := nipost.Post{ @@ -842,6 +862,7 @@ func TestBuilder_PublishActivationTx_NoPrevATX_PublishFails_InitialPost_preserve vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPosAtx)) + tab.atxsdata.AddFromHeader(vPosAtx.ToHeader(), nipostData.VRFNonce, false) // generate and store initial refPost in state refPost := nipost.Post{ @@ -949,6 +970,7 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { vPosAtx, err := posAtx.Verify(0, 2) r.NoError(err) r.NoError(atxs.Add(tab.db, vPosAtx)) + tab.atxsdata.AddFromHeader(vPosAtx.ToHeader(), nipostData.VRFNonce, false) challenge = types.NIPostChallenge{ Sequence: 0, @@ -964,6 +986,7 @@ func TestBuilder_PublishActivationTx_PrevATXWithoutPrevATX(t *testing.T) { vPrevAtx, err := prevAtx.Verify(0, 1) r.NoError(err) r.NoError(atxs.Add(tab.db, vPrevAtx)) + tab.atxsdata.AddFromHeader(vPrevAtx.ToHeader(), nipostData.VRFNonce, false) // Act tab.msync.EXPECT().RegisterForATXSynced().DoAndReturn(closedChan).AnyTimes() @@ -1064,6 +1087,7 @@ func TestBuilder_PublishActivationTx_TargetsEpochBasedOnPosAtx(t *testing.T) { vPosAtx, err := posAtx.Verify(0, 1) r.NoError(err) r.NoError(atxs.Add(tab.db, vPosAtx)) + tab.atxsdata.AddFromHeader(vPosAtx.ToHeader(), nipostData.VRFNonce, false) // Act & Assert tab.msync.EXPECT().RegisterForATXSynced().DoAndReturn(closedChan).AnyTimes() @@ -1176,6 +1200,7 @@ func TestBuilder_PublishActivationTx_FailsWhenNIPostBuilderFails(t *testing.T) { vPosAtx, err := posAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPosAtx)) + tab.atxsdata.AddFromHeader(vPosAtx.ToHeader(), nipostData.VRFNonce, false) tab.mclock.EXPECT().CurrentLayer().Return(posEpoch.FirstLayer()).AnyTimes() tab.mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( @@ -1299,6 +1324,7 @@ func TestBuilder_RetryPublishActivationTx(t *testing.T) { vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPrevAtx)) + tab.atxsdata.AddFromHeader(vPrevAtx.ToHeader(), nipostData.VRFNonce, false) publishEpoch := posEpoch + 1 currLayer := posEpoch.FirstLayer() @@ -1459,6 +1485,7 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) { vPrevAtx, err := prevAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vPrevAtx)) + tab.atxsdata.AddFromHeader(vPrevAtx.ToHeader(), nipost.VRFNonce, false) currLayer := posEpoch.FirstLayer().Add(1) tab.mclock.EXPECT().CurrentLayer().Return(currLayer).AnyTimes() @@ -1693,6 +1720,7 @@ func TestGetPositioningAtxPicksAtxWithValidChain(t *testing.T) { vInvalidAtx, err := invalidAtx.Verify(0, 100) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vInvalidAtx)) + tab.atxsdata.AddFromHeader(vInvalidAtx.ToHeader(), nipostData.VRFNonce, false) // Valid chain with lower height sigValid, err := signing.NewEdSigner() @@ -1703,6 +1731,7 @@ func TestGetPositioningAtxPicksAtxWithValidChain(t *testing.T) { vValidAtx, err := validAtx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(tab.db, vValidAtx)) + tab.atxsdata.AddFromHeader(vValidAtx.ToHeader(), nipostData.VRFNonce, false) tab.mValidator.EXPECT(). VerifyChain(gomock.Any(), invalidAtx.ID(), tab.goldenATXID, gomock.Any()). @@ -1710,12 +1739,12 @@ 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(), 77) + posAtxID, err := tab.getPositioningAtx(context.Background(), sig.NodeID(), 77, nil) require.NoError(t, err) require.Equal(t, posAtxID, vValidAtx.ID()) // should use the cached positioning ATX when asked for the same publish epoch - posAtxID, err = tab.getPositioningAtx(context.Background(), sig.NodeID(), 77) + posAtxID, err = tab.getPositioningAtx(context.Background(), sig.NodeID(), 77, nil) require.NoError(t, err) require.Equal(t, posAtxID, vValidAtx.ID()) @@ -1726,21 +1755,98 @@ 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(), 99) + posAtxID, err = tab.getPositioningAtx(context.Background(), sig.NodeID(), 99, nil) require.NoError(t, err) require.Equal(t, posAtxID, vValidAtx.ID()) } -func TestGetPositioningAtxDbFailed(t *testing.T) { - tab := newTestBuilder(t, 1) - sig := maps.Values(tab.signers)[0] +func TestGetPositioningAtx(t *testing.T) { + t.Parallel() + t.Run("db failed", func(t *testing.T) { + t.Parallel() + tab := newTestBuilder(t, 1) - db := sqlmocks.NewMockExecutor(gomock.NewController(t)) - tab.Builder.db = db - expected := errors.New("db error") - db.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Any()).Return(0, expected) + db := sqlmocks.NewMockExecutor(gomock.NewController(t)) + tab.Builder.db = db + 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(), 99) - require.ErrorIs(t, err, expected) - require.Equal(t, types.ATXID{}, none) + none, err := tab.getPositioningAtx(context.Background(), types.EmptyNodeID, 99, nil) + require.ErrorIs(t, err, expected) + require.Equal(t, types.ATXID{}, none) + }) + t.Run("picks golden if no ATXs", func(t *testing.T) { + tab := newTestBuilder(t, 1) + atx, err := tab.getPositioningAtx(context.Background(), types.EmptyNodeID, 99, nil) + require.NoError(t, err) + require.Equal(t, tab.goldenATXID, atx) + }) + t.Run("prefers own previous to golden", func(t *testing.T) { + prev := &types.VerifiedActivationTx{ActivationTx: &types.ActivationTx{}} + prev.SetID(types.RandomATXID()) + tab := newTestBuilder(t, 1) + atx, err := tab.getPositioningAtx(context.Background(), types.EmptyNodeID, 99, prev) + require.NoError(t, err) + require.Equal(t, prev.ID(), atx) + }) + t.Run("prefers own previous when it has GTE ticks", func(t *testing.T) { + tab := newTestBuilder(t, 1) + + atxInDb := &types.ActivationTx{InnerActivationTx: types.InnerActivationTx{NumUnits: 1}} + atxInDb.SetID(types.RandomATXID()) + vAtxInDb := toVerified(t, atxInDb, 10) + require.NoError(t, atxs.Add(tab.db, vAtxInDb)) + tab.atxsdata.AddFromHeader(vAtxInDb.ToHeader(), 0, false) + + prev := &types.ActivationTx{InnerActivationTx: types.InnerActivationTx{NumUnits: 1}} + prev.SetID(types.RandomATXID()) + vPrev := toVerified(t, prev, 100) + + tab.mValidator.EXPECT().VerifyChain(gomock.Any(), atxInDb.ID(), tab.goldenATXID, gomock.Any()) + found, err := tab.searchPositioningAtx(context.Background(), types.EmptyNodeID, 99) + require.NoError(t, err) + require.Equal(t, atxInDb.ID(), found) + + // prev.Height > found.Height + selected, err := tab.getPositioningAtx(context.Background(), types.EmptyNodeID, 99, vPrev) + require.NoError(t, err) + require.Equal(t, prev.ID(), selected) + + // prev.Height == found.Height + vPrev = toVerified(t, prev, vAtxInDb.TickCount()) + selected, err = tab.getPositioningAtx(context.Background(), types.EmptyNodeID, 99, vPrev) + require.NoError(t, err) + require.Equal(t, prev.ID(), selected) + }) +} + +func TestFindFullyValidHighTickAtx(t *testing.T) { + t.Parallel() + golden := types.RandomATXID() + + t.Run("skips malicious ATXs", func(t *testing.T) { + data := atxsdata.New() + atxMal := &types.ActivationTx{ + SmesherID: types.RandomNodeID(), + InnerActivationTx: types.InnerActivationTx{NumUnits: 1}, + } + atxMal.SetID(types.RandomATXID()) + vAtxMal := toVerified(t, atxMal, 100) + data.AddFromHeader(vAtxMal.ToHeader(), 0, true) + + atxLower := &types.ActivationTx{ + SmesherID: types.RandomNodeID(), + InnerActivationTx: types.InnerActivationTx{NumUnits: 1}, + } + atxLower.SetID(types.RandomATXID()) + vAtxLower := toVerified(t, atxLower, 10) + data.AddFromHeader(vAtxLower.ToHeader(), 0, false) + + mValidator := NewMocknipostValidator(gomock.NewController(t)) + mValidator.EXPECT().VerifyChain(gomock.Any(), atxLower.ID(), golden, gomock.Any()) + + found, err := findFullyValidHighTickAtx(context.Background(), data, 0, golden, mValidator, zaptest.NewLogger(t)) + require.NoError(t, err) + require.Equal(t, atxLower.ID(), found) + }) } diff --git a/activation/e2e/activation_test.go b/activation/e2e/activation_test.go index f7dd88f15f..ce23b76079 100644 --- a/activation/e2e/activation_test.go +++ b/activation/e2e/activation_test.go @@ -3,6 +3,7 @@ package activation_test import ( "context" "sync" + "sync/atomic" "testing" "time" @@ -10,19 +11,21 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "go.uber.org/zap" "go.uber.org/zap/zaptest" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/activation" "github.com/spacemeshos/go-spacemesh/activation/wire" "github.com/spacemeshos/go-spacemesh/api/grpcserver" + "github.com/spacemeshos/go-spacemesh/atxsdata" "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/atxs" "github.com/spacemeshos/go-spacemesh/sql/localsql" "github.com/spacemeshos/go-spacemesh/timesync" ) @@ -30,7 +33,10 @@ import ( func Test_BuilderWithMultipleClients(t *testing.T) { ctrl := gomock.NewController(t) - numSigners := 3 + const numEpochs = 3 + const numSigners = 3 + const totalAtxs = numEpochs * numSigners + signers := make(map[types.NodeID]*signing.EdSigner, numSigners) for range numSigners { sig, err := signing.NewEdSigner() @@ -43,7 +49,6 @@ func Test_BuilderWithMultipleClients(t *testing.T) { goldenATX := types.ATXID{2, 3, 4} cfg := activation.DefaultPostConfig() db := sql.InMemory() - cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) syncer := activation.NewMocksyncer(ctrl) syncer.EXPECT().RegisterForATXSynced().DoAndReturn(func() <-chan struct{} { @@ -70,7 +75,7 @@ func Test_BuilderWithMultipleClients(t *testing.T) { i += 1 eg.Go(func() error { validator := activation.NewMocknipostValidator(ctrl) - mgr, err := activation.NewPostSetupManager(cfg, logger, cdb, goldenATX, syncer, validator) + mgr, err := activation.NewPostSetupManager(cfg, logger, db, atxsdata.New(), goldenATX, syncer, validator) require.NoError(t, err) initPost(t, mgr, opts, sig.NodeID()) @@ -122,7 +127,10 @@ func Test_BuilderWithMultipleClients(t *testing.T) { localDB, poetDb, svc, - []types.PoetServer{{Pubkey: types.NewBase64Enc([]byte("foobar")), Address: poetProver.RestURL().String()}}, + []types.PoetServer{{ + Pubkey: types.NewBase64Enc(poetProver.Service.PublicKey()), + Address: poetProver.RestURL().String(), + }}, logger.Named("nipostBuilder"), poetCfg, clock, @@ -135,31 +143,42 @@ func Test_BuilderWithMultipleClients(t *testing.T) { RegossipInterval: 0, } + data := atxsdata.New() + var atxsPublished atomic.Uint32 var atxMtx sync.Mutex - atxs := make(map[types.NodeID]types.ActivationTx) + gotAtxs := 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() + gotAtx, err := wire.ActivationTxFromBytes(got) require.NoError(t, err) - atxs[gotAtx.SmesherID] = *gotAtx - if len(atxs) == numSigners { + gotAtxs[gotAtx.SmesherID] = append(gotAtxs[gotAtx.SmesherID], *gotAtx) + gotAtx.SetReceived(time.Now()) + gotAtx.SetEffectiveNumUnits(gotAtx.NumUnits) + vAtx, err := gotAtx.Verify(0, 100) + require.NoError(t, err) + require.NoError(t, atxs.Add(db, vAtx)) + data.AddFromHeader(vAtx.ToHeader(), 0, false) + + if atxsPublished.Add(1) == totalAtxs { close(endChan) } return nil }, - ).Times(numSigners) + ).Times(totalAtxs) verifier, err := activation.NewPostVerifier(cfg, logger.Named("verifier")) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, verifier.Close()) }) - v := activation.NewValidator(nil, poetDb, cfg, opts.Scrypt, verifier) + v := activation.NewValidator(db, poetDb, cfg, opts.Scrypt, verifier) tab := activation.NewBuilder( conf, - cdb, + db, + data, localDB, mpub, nb, @@ -177,7 +196,13 @@ func Test_BuilderWithMultipleClients(t *testing.T) { // initial proof postStates.EXPECT().Set(sig.NodeID(), types.PostStateProving), postStates.EXPECT().Set(sig.NodeID(), types.PostStateIdle), - // post proof + // post proof - 1st epoch + postStates.EXPECT().Set(sig.NodeID(), types.PostStateProving), + postStates.EXPECT().Set(sig.NodeID(), types.PostStateIdle), + // 2nd epoch + postStates.EXPECT().Set(sig.NodeID(), types.PostStateProving), + postStates.EXPECT().Set(sig.NodeID(), types.PostStateIdle), + // 3rd epoch postStates.EXPECT().Set(sig.NodeID(), types.PostStateProving), postStates.EXPECT().Set(sig.NodeID(), types.PostStateIdle), ) @@ -189,33 +214,44 @@ func Test_BuilderWithMultipleClients(t *testing.T) { require.NoError(t, tab.StopSmeshing(false)) for _, sig := range signers { - atx := atxs[sig.NodeID()] - - _, err = v.NIPost( - context.Background(), - sig.NodeID(), - *atx.CommitmentATX, - atx.NIPost, - wire.NIPostChallengeToWireV1(&atx.NIPostChallenge).Hash(), - atx.NumUnits, - ) - require.NoError(t, err) + var commitment types.ATXID + var previous types.ATXID - err := v.VRFNonce( - sig.NodeID(), - *atx.CommitmentATX, - atx.VRFNonce, - atx.NIPost.PostMetadata, - atx.NumUnits, - ) - require.NoError(t, err) + for seq, atx := range gotAtxs[sig.NodeID()] { + logger.Debug("checking ATX", zap.Inline(&atx), zap.Uint64("seq", uint64(seq))) + if seq == 0 { + commitment = *atx.CommitmentATX + require.Equal(t, sig.NodeID(), *atx.NodeID) + require.Equal(t, goldenATX, atx.PositioningATX) + require.NotNil(t, atx.VRFNonce) + err := v.VRFNonce( + sig.NodeID(), + commitment, + atx.VRFNonce, + atx.NIPost.PostMetadata, + atx.NumUnits, + ) + require.NoError(t, err) + } else { + require.Nil(t, atx.VRFNonce) + require.Equal(t, previous, atx.PositioningATX) + } + _, err = v.NIPost( + context.Background(), + sig.NodeID(), + commitment, + atx.NIPost, + wire.NIPostChallengeToWireV1(&atx.NIPostChallenge).Hash(), + atx.NumUnits, + ) + require.NoError(t, err) - require.Equal(t, postGenesisEpoch, atx.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, previous, atx.PrevATXID) + require.Equal(t, postGenesisEpoch.Add(uint32(seq)), atx.PublishEpoch+1) + require.Equal(t, uint64(seq), atx.Sequence) + require.Equal(t, types.Address{}, atx.Coinbase) - require.Equal(t, types.Address{}, atx.Coinbase) - require.Equal(t, sig.NodeID(), *atx.NodeID) + previous = atx.ID() + } } } diff --git a/activation/e2e/nipost_test.go b/activation/e2e/nipost_test.go index 872a88fc0e..78f7adf809 100644 --- a/activation/e2e/nipost_test.go +++ b/activation/e2e/nipost_test.go @@ -19,6 +19,7 @@ import ( "github.com/spacemeshos/go-spacemesh/activation" "github.com/spacemeshos/go-spacemesh/api/grpcserver" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/log" @@ -129,7 +130,7 @@ func TestNIPostBuilderWithClients(t *testing.T) { }) validator := activation.NewMocknipostValidator(ctrl) - mgr, err := activation.NewPostSetupManager(cfg, logger, cdb, goldenATX, syncer, validator) + mgr, err := activation.NewPostSetupManager(cfg, logger, cdb, atxsdata.New(), goldenATX, syncer, validator) require.NoError(t, err) opts := activation.DefaultPostSetupOpts() @@ -263,7 +264,6 @@ func TestNewNIPostBuilderNotInitialized(t *testing.T) { goldenATX := types.ATXID{2, 3, 4} cfg := activation.DefaultPostConfig() db := sql.InMemory() - cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) syncer := activation.NewMocksyncer(ctrl) syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { @@ -273,7 +273,7 @@ func TestNewNIPostBuilderNotInitialized(t *testing.T) { }) validator := activation.NewMocknipostValidator(ctrl) - mgr, err := activation.NewPostSetupManager(cfg, logger, cdb, goldenATX, syncer, validator) + mgr, err := activation.NewPostSetupManager(cfg, logger, db, atxsdata.New(), goldenATX, syncer, validator) require.NoError(t, err) // ensure that genesis aligns with layer timings @@ -368,7 +368,6 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { goldenATX := types.ATXID{2, 3, 4} cfg := activation.DefaultPostConfig() db := sql.InMemory() - cdb := datastore.NewCachedDB(db, log.NewFromLog(logger)) syncer := activation.NewMocksyncer(ctrl) syncer.EXPECT().RegisterForATXSynced().AnyTimes().DoAndReturn(func() <-chan struct{} { @@ -391,7 +390,7 @@ func Test_NIPostBuilderWithMultipleClients(t *testing.T) { for _, sig := range signers { opts := opts eg.Go(func() error { - mgr, err := activation.NewPostSetupManager(cfg, logger, cdb, goldenATX, syncer, validator) + mgr, err := activation.NewPostSetupManager(cfg, logger, db, atxsdata.New(), goldenATX, syncer, validator) require.NoError(t, err) opts.DataDir = t.TempDir() diff --git a/activation/e2e/validation_test.go b/activation/e2e/validation_test.go index dbfafe3adf..9552489949 100644 --- a/activation/e2e/validation_test.go +++ b/activation/e2e/validation_test.go @@ -14,8 +14,8 @@ import ( "github.com/spacemeshos/go-spacemesh/activation" "github.com/spacemeshos/go-spacemesh/api/grpcserver" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/log" "github.com/spacemeshos/go-spacemesh/signing" "github.com/spacemeshos/go-spacemesh/sql" @@ -31,7 +31,7 @@ func TestValidator_Validate(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() validator := activation.NewMocknipostValidator(gomock.NewController(t)) syncer := activation.NewMocksyncer(gomock.NewController(t)) @@ -41,7 +41,7 @@ func TestValidator_Validate(t *testing.T) { return synced }) - mgr, err := activation.NewPostSetupManager(cfg, logger, cdb, goldenATX, syncer, validator) + mgr, err := activation.NewPostSetupManager(cfg, logger, db, atxsdata.New(), goldenATX, syncer, validator) require.NoError(t, err) opts := activation.DefaultPostSetupOpts() @@ -109,7 +109,7 @@ func TestValidator_Validate(t *testing.T) { nipost, err := nb.BuildNIPost(context.Background(), sig, postGenesisEpoch+2, challenge) require.NoError(t, err) - v := activation.NewValidator(cdb, poetDb, cfg, opts.Scrypt, verifier) + v := activation.NewValidator(db, poetDb, cfg, opts.Scrypt, verifier) _, err = v.NIPost(context.Background(), sig.NodeID(), goldenATX, nipost.NIPost, challenge, nipost.NumUnits) require.NoError(t, err) @@ -130,7 +130,7 @@ func TestValidator_Validate(t *testing.T) { newPostCfg := cfg newPostCfg.MinNumUnits = nipost.NumUnits + 1 - v = activation.NewValidator(cdb, poetDb, newPostCfg, opts.Scrypt, nil) + v = activation.NewValidator(db, poetDb, newPostCfg, opts.Scrypt, nil) _, err = v.NIPost(context.Background(), sig.NodeID(), goldenATX, nipost.NIPost, challenge, nipost.NumUnits) require.EqualError( t, @@ -140,7 +140,7 @@ func TestValidator_Validate(t *testing.T) { newPostCfg = cfg newPostCfg.MaxNumUnits = nipost.NumUnits - 1 - v = activation.NewValidator(cdb, poetDb, newPostCfg, opts.Scrypt, nil) + v = activation.NewValidator(db, poetDb, newPostCfg, opts.Scrypt, nil) _, err = v.NIPost(context.Background(), sig.NodeID(), goldenATX, nipost.NIPost, challenge, nipost.NumUnits) require.EqualError( t, @@ -150,7 +150,7 @@ func TestValidator_Validate(t *testing.T) { newPostCfg = cfg newPostCfg.LabelsPerUnit = nipost.PostMetadata.LabelsPerUnit + 1 - v = activation.NewValidator(cdb, poetDb, newPostCfg, opts.Scrypt, nil) + v = activation.NewValidator(db, poetDb, newPostCfg, opts.Scrypt, nil) _, err = v.NIPost(context.Background(), sig.NodeID(), goldenATX, nipost.NIPost, challenge, nipost.NumUnits) require.EqualError( t, diff --git a/activation/poet.go b/activation/poet.go index c298604020..4eb4c83e86 100644 --- a/activation/poet.go +++ b/activation/poet.go @@ -355,10 +355,12 @@ func (c *PoetClient) Proof(ctx context.Context, roundID string) (*types.PoetProo defer c.gettingProof.Unlock() if members, ok := c.proofMembers[roundID]; ok { - if proof, err := c.db.ProofForRound(c.id, roundID); err == nil { + proof, err := c.db.ProofForRound(c.id, roundID) + if err == nil { c.logger.Debug("returning cached proof", zap.String("round_id", roundID)) return proof, members, nil } + c.logger.Warn("cached members found but proof not found in db", zap.String("round_id", roundID), zap.Error(err)) } getProofsCtx, cancel := withConditionalTimeout(ctx, c.requestTimeout) diff --git a/activation/post.go b/activation/post.go index 9b8cdce06b..08f083592a 100644 --- a/activation/post.go +++ b/activation/post.go @@ -12,8 +12,8 @@ import ( "github.com/spacemeshos/post/initialization" "go.uber.org/zap" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/events" "github.com/spacemeshos/go-spacemesh/metrics/public" "github.com/spacemeshos/go-spacemesh/sql" @@ -180,7 +180,8 @@ type PostSetupManager struct { cfg PostConfig logger *zap.Logger - db *datastore.CachedDB + db sql.Executor + atxsdata *atxsdata.Data goldenATXID types.ATXID validator nipostValidator @@ -207,7 +208,8 @@ func PostValidityDelay(delay time.Duration) PostSetupManagerOpt { func NewPostSetupManager( cfg PostConfig, logger *zap.Logger, - db *datastore.CachedDB, + db sql.Executor, + atxsdata *atxsdata.Data, goldenATXID types.ATXID, syncer syncer, validator nipostValidator, @@ -217,6 +219,7 @@ func NewPostSetupManager( cfg: cfg, logger: logger, db: db, + atxsdata: atxsdata, goldenATXID: goldenATXID, state: PostSetupStateNotStarted, syncer: syncer, @@ -397,10 +400,15 @@ func (mgr *PostSetupManager) findCommitmentAtx(ctx context.Context) (types.ATXID mgr.logger.Info("ATXs synced - selecting commitment ATX") } + latest, err := atxs.LatestEpoch(mgr.db) + if err != nil { + return types.EmptyATXID, fmt.Errorf("get latest epoch: %w", err) + } + atx, err := findFullyValidHighTickAtx( context.Background(), - mgr.db, - types.EmptyNodeID, + mgr.atxsdata, + latest, mgr.goldenATXID, mgr.validator, mgr.logger, @@ -408,7 +416,7 @@ func (mgr *PostSetupManager) findCommitmentAtx(ctx context.Context) (types.ATXID VerifyChainOpts.WithLogger(mgr.logger), ) switch { - case errors.Is(err, sql.ErrNotFound): + case errors.Is(err, ErrNotFound): mgr.logger.Info("using golden atx as commitment atx") return mgr.goldenATXID, nil case err != nil: diff --git a/activation/post_supervisor_test.go b/activation/post_supervisor_test.go index 8ea9d6bf27..3e9c1d6551 100644 --- a/activation/post_supervisor_test.go +++ b/activation/post_supervisor_test.go @@ -21,9 +21,8 @@ import ( "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/datastore" - "github.com/spacemeshos/go-spacemesh/log/logtest" "github.com/spacemeshos/go-spacemesh/signing" "github.com/spacemeshos/go-spacemesh/sql" ) @@ -57,8 +56,9 @@ func newPostManager(t *testing.T, cfg PostConfig, opts PostSetupOpts) *PostSetup close(ch) return ch }) - cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(t)) - mgr, err := NewPostSetupManager(cfg, zaptest.NewLogger(t), cdb, types.RandomATXID(), syncer, validator) + db := sql.InMemory() + atxsdata := atxsdata.New() + mgr, err := NewPostSetupManager(cfg, zaptest.NewLogger(t), db, atxsdata, types.RandomATXID(), syncer, validator) require.NoError(t, err) return mgr } diff --git a/activation/post_test.go b/activation/post_test.go index d1abc3d1e6..db11d79695 100644 --- a/activation/post_test.go +++ b/activation/post_test.go @@ -13,6 +13,7 @@ import ( "go.uber.org/zap/zaptest" "golang.org/x/sync/errgroup" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/log/logtest" @@ -283,6 +284,7 @@ func TestPostSetupManager_findCommitmentAtx_UsesLatestAtx(t *testing.T) { vAtx, err := atx.Verify(0, 1) require.NoError(t, err) require.NoError(t, atxs.Add(mgr.db, vAtx)) + mgr.atxsdata.AddFromHeader(vAtx.ToHeader(), 0, false) commitmentAtx, err := mgr.findCommitmentAtx(context.Background()) require.NoError(t, err) @@ -365,7 +367,8 @@ func newTestPostManager(tb testing.TB) *testPostManager { syncer.EXPECT().RegisterForATXSynced().AnyTimes().Return(synced) cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(tb)) - mgr, err := NewPostSetupManager(DefaultPostConfig(), zaptest.NewLogger(tb), cdb, goldenATXID, syncer, validator) + logger := zaptest.NewLogger(tb) + mgr, err := NewPostSetupManager(DefaultPostConfig(), logger, cdb, atxsdata.New(), goldenATXID, syncer, validator) require.NoError(tb, err) return &testPostManager{ diff --git a/api/grpcserver/post_service_test.go b/api/grpcserver/post_service_test.go index a67d52a730..27559c1976 100644 --- a/api/grpcserver/post_service_test.go +++ b/api/grpcserver/post_service_test.go @@ -20,9 +20,8 @@ import ( "google.golang.org/grpc/status" "github.com/spacemeshos/go-spacemesh/activation" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/datastore" - "github.com/spacemeshos/go-spacemesh/log/logtest" "github.com/spacemeshos/go-spacemesh/signing" "github.com/spacemeshos/go-spacemesh/sql" ) @@ -59,8 +58,9 @@ func launchPostSupervisor( close(ch) return ch }) - cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(tb)) - mgr, err := activation.NewPostSetupManager(postCfg, log.Named("post manager"), cdb, goldenATXID, syncer, validator) + db := sql.InMemory() + logger := log.Named("post manager") + mgr, err := activation.NewPostSetupManager(postCfg, logger, db, atxsdata.New(), goldenATXID, syncer, validator) require.NoError(tb, err) // start post supervisor @@ -102,8 +102,9 @@ func launchPostSupervisorTLS( close(ch) return ch }) - cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(tb)) - mgr, err := activation.NewPostSetupManager(postCfg, log.Named("post manager"), cdb, goldenATXID, syncer, validator) + db := sql.InMemory() + logger := log.Named("post supervisor") + mgr, err := activation.NewPostSetupManager(postCfg, logger, db, atxsdata.New(), goldenATXID, syncer, validator) require.NoError(tb, err) // start post supervisor diff --git a/atxsdata/data.go b/atxsdata/data.go index 6b07666a9b..caea15cee6 100644 --- a/atxsdata/data.go +++ b/atxsdata/data.go @@ -1,6 +1,7 @@ package atxsdata import ( + "slices" "sync" "sync/atomic" @@ -169,6 +170,16 @@ func (d *Data) Get(epoch types.EpochID, atx types.ATXID) *ATX { return data } +func (d *Data) Size(target types.EpochID) int { + d.mu.RLock() + defer d.mu.RUnlock() + ecache, exists := d.epochs[target] + if !exists { + return 0 + } + return len(ecache.index) +} + type lockGuard struct{} // AtxFilter is a function that filters atxs. @@ -204,6 +215,33 @@ func (d *Data) IterateInEpoch(epoch types.EpochID, fn func(types.ATXID, *ATX), f } } +func (d *Data) IterateHighTicksInEpoch(target types.EpochID, fn func(types.ATXID) bool) { + type candidate struct { + id types.ATXID + *ATX + } + candidates := make([]candidate, 0, d.Size(target)) + d.IterateInEpoch(target, func(id types.ATXID, atx *ATX) { + candidates = append(candidates, candidate{id: id, ATX: atx}) + }, NotMalicious) + + slices.SortFunc(candidates, func(a, b candidate) int { + switch { + case a.Height < b.Height: + return 1 + case a.Height > b.Height: + return -1 + } + return 0 + }) + + for _, c := range candidates { + if cont := fn(c.id); !cont { + return + } + } +} + func (d *Data) MissingInEpoch(epoch types.EpochID, atxs []types.ATXID) []types.ATXID { d.mu.RLock() defer d.mu.RUnlock() diff --git a/node/node.go b/node/node.go index d143e661e4..36b56d1759 100644 --- a/node/node.go +++ b/node/node.go @@ -995,7 +995,8 @@ func (app *App) initServices(ctx context.Context) error { postSetupMgr, err := activation.NewPostSetupManager( app.Config.POST, app.addLogger(PostLogger, lg).Zap(), - app.cachedDB, + app.db, + app.atxsdata, goldenATXID, newSyncer, app.validator, @@ -1030,7 +1031,8 @@ func (app *App) initServices(ctx context.Context) error { } atxBuilder := activation.NewBuilder( builderConfig, - app.cachedDB, + app.db, + app.atxsdata, app.localDB, app.host, nipostBuilder, diff --git a/node/node_test.go b/node/node_test.go index 16faeca3a6..84dd7dbfc4 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -1106,7 +1106,8 @@ func TestAdminEvents_MultiSmesher(t *testing.T) { mgr, err := activation.NewPostSetupManager( cfg.POST, logger.Zap(), - app.cachedDB, + app.db, + app.atxsdata, types.ATXID(app.Config.Genesis.GoldenATX()), app.syncer, app.validator, diff --git a/systest/tests/distributed_post_verification_test.go b/systest/tests/distributed_post_verification_test.go index 1e52808a95..3d9b52274a 100644 --- a/systest/tests/distributed_post_verification_test.go +++ b/systest/tests/distributed_post_verification_test.go @@ -24,6 +24,7 @@ import ( "github.com/spacemeshos/go-spacemesh/activation" "github.com/spacemeshos/go-spacemesh/activation/wire" "github.com/spacemeshos/go-spacemesh/api/grpcserver" + "github.com/spacemeshos/go-spacemesh/atxsdata" "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/datastore" @@ -123,6 +124,7 @@ func TestPostMalfeasanceProof(t *testing.T) { cfg.POST, logger.Named("post"), datastore.NewCachedDB(sql.InMemory(), log.NewNop()), + atxsdata.New(), cl.GoldenATX(), syncer, activation.NewMocknipostValidator(ctrl),