Skip to content

Commit

Permalink
E2E test for publishing ATX after a checkpoint (#6090)
Browse files Browse the repository at this point in the history
## Motivation

Currently, this scenario is indirectly covered only by a long systest. The E2E is quicker (13s) and easier to understand and debug.
  • Loading branch information
poszu committed Jul 3, 2024
1 parent 7c41469 commit 070fdc8
Show file tree
Hide file tree
Showing 12 changed files with 475 additions and 320 deletions.
4 changes: 2 additions & 2 deletions activation/activation.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ func (b *Builder) SmesherIDs() []types.NodeID {
return maps.Keys(b.signers)
}

func (b *Builder) buildInitialPost(ctx context.Context, nodeID types.NodeID) 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 := atxs.GetLastIDByNodeID(b.db, nodeID); err == nil {
return nil
Expand Down Expand Up @@ -391,7 +391,7 @@ func (b *Builder) buildInitialPost(ctx context.Context, nodeID types.NodeID) err

func (b *Builder) buildPost(ctx context.Context, nodeID types.NodeID) error {
for {
err := b.buildInitialPost(ctx, nodeID)
err := b.BuildInitialPost(ctx, nodeID)
if err == nil {
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions activation/activation_multi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,10 @@ func Test_Builder_Multi_InitialPost(t *testing.T) {
},
nil,
)
require.NoError(t, tab.buildInitialPost(context.Background(), 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(), sig.NodeID()))
require.NoError(t, tab.BuildInitialPost(context.Background(), sig.NodeID()))
return nil
})
}
Expand Down
12 changes: 6 additions & 6 deletions activation/activation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1173,9 +1173,9 @@ func TestBuilder_InitialProofGeneratedOnce(t *testing.T) {
tab.mValidator.EXPECT().
PostV2(gomock.Any(), sig.NodeID(), post.CommitmentATX, initialPost, shared.ZeroChallenge, post.NumUnits)

require.NoError(t, tab.buildInitialPost(context.Background(), 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(), sig.NodeID()))
require.NoError(t, tab.BuildInitialPost(context.Background(), sig.NodeID()))
}

func TestBuilder_InitialPostIsPersisted(t *testing.T) {
Expand Down Expand Up @@ -1205,10 +1205,10 @@ func TestBuilder_InitialPostIsPersisted(t *testing.T) {
tab.mValidator.EXPECT().
PostV2(gomock.Any(), sig.NodeID(), commitmentATX, initialPost, shared.ZeroChallenge, numUnits)

require.NoError(t, tab.buildInitialPost(context.Background(), 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(), sig.NodeID()))
require.NoError(t, tab.BuildInitialPost(context.Background(), sig.NodeID()))
}

func TestBuilder_InitialPostLogErrorMissingVRFNonce(t *testing.T) {
Expand All @@ -1235,7 +1235,7 @@ func TestBuilder_InitialPostLogErrorMissingVRFNonce(t *testing.T) {
)
tab.mValidator.EXPECT().
PostV2(gomock.Any(), sig.NodeID(), commitmentATX, initialPost, shared.ZeroChallenge, numUnits)
err := tab.buildInitialPost(context.Background(), sig.NodeID())
err := tab.BuildInitialPost(context.Background(), sig.NodeID())
require.ErrorIs(t, err, errNilVrfNonce)

observedLogs := tab.observedLogs.FilterLevelExact(zapcore.ErrorLevel)
Expand All @@ -1258,7 +1258,7 @@ func TestBuilder_InitialPostLogErrorMissingVRFNonce(t *testing.T) {
},
nil,
)
require.NoError(t, tab.buildInitialPost(context.Background(), sig.NodeID()))
require.NoError(t, tab.BuildInitialPost(context.Background(), sig.NodeID()))
}

func TestWaitPositioningAtx(t *testing.T) {
Expand Down
74 changes: 22 additions & 52 deletions activation/e2e/activation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,59 +33,47 @@ import (
"github.com/spacemeshos/go-spacemesh/timesync"
)

func syncedSyncer(t testing.TB) *activation.Mocksyncer {
syncer := activation.NewMocksyncer(gomock.NewController(t))
syncer.EXPECT().RegisterForATXSynced().DoAndReturn(func() <-chan struct{} {
synced := make(chan struct{})
close(synced)
return synced
}).AnyTimes()
return syncer
}

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

const numEpochs = 3
const numSigners = 3
const totalAtxs = numEpochs * numSigners

signers := make(map[types.NodeID]*signing.EdSigner, numSigners)
for range numSigners {
signers := make([]*signing.EdSigner, numSigners)
for i := range numSigners {
sig, err := signing.NewEdSigner()
require.NoError(t, err)

signers[sig.NodeID()] = sig
signers[i] = sig
}

logger := zaptest.NewLogger(t)
goldenATX := types.ATXID{2, 3, 4}
cfg := activation.DefaultPostConfig()
cfg := testPostConfig()
db := sql.InMemory()
localDB := localsql.InMemory()

syncer := activation.NewMocksyncer(ctrl)
syncer.EXPECT().RegisterForATXSynced().DoAndReturn(func() <-chan struct{} {
synced := make(chan struct{})
close(synced)
return synced
}).AnyTimes()

svc := grpcserver.NewPostService(logger)
svc.AllowConnections(true)
grpcCfg, cleanup := launchServer(t, svc)
t.Cleanup(cleanup)

var eg errgroup.Group
i := uint32(1)
opts := testPostSetupOpts(t)
for _, sig := range signers {
opts := opts
opts.DataDir = t.TempDir()
opts.NumUnits = min(i*2, cfg.MaxNumUnits)
i += 1
for i, sig := range signers {
opts := testPostSetupOpts(t)
opts.NumUnits = min(opts.NumUnits+2*uint32(i), cfg.MaxNumUnits)
eg.Go(func() error {
validator := activation.NewMocknipostValidator(ctrl)
mgr, err := activation.NewPostSetupManager(cfg, logger, db, atxsdata.New(), goldenATX, syncer, validator)
require.NoError(t, err)

initPost(t, mgr, opts, sig.NodeID())
t.Cleanup(launchPostSupervisor(t, logger, mgr, sig, 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")
initPost(t, cfg, opts, sig, goldenATX, grpcCfg, svc)
return nil
})
}
Expand All @@ -104,14 +92,15 @@ func Test_BuilderWithMultipleClients(t *testing.T) {
MaxRequestRetries: 10,
}

scrypt := testPostSetupOpts(t).Scrypt
pubkey, address := spawnTestCertifier(
t,
cfg,
func(id []byte) *poetShared.Cert {
exp := time.Now().Add(epoch)
return &poetShared.Cert{Pubkey: id, Expiration: &exp}
},
verifying.WithLabelScryptParams(opts.Scrypt),
verifying.WithLabelScryptParams(scrypt),
)

poetProver := spawnPoet(
Expand Down Expand Up @@ -147,19 +136,17 @@ func Test_BuilderWithMultipleClients(t *testing.T) {
db,
poetDb,
cfg,
opts.Scrypt,
scrypt,
verifier,
)

postStates := activation.NewMockPostStates(ctrl)
nb, err := activation.NewNIPostBuilder(
localDB,
svc,
logger.Named("nipostBuilder"),
poetCfg,
clock,
validator,
activation.NipostbuilderWithPostStates(postStates),
activation.WithPoetServices(client),
)
require.NoError(t, err)
Expand Down Expand Up @@ -208,30 +195,13 @@ func Test_BuilderWithMultipleClients(t *testing.T) {
mpub,
nb,
clock,
syncer,
syncedSyncer(t),
logger,
activation.WithPoetConfig(poetCfg),
activation.WithValidator(validator),
activation.WithPostStates(postStates),
activation.WithPoets(client),
)
for _, sig := range signers {
gomock.InOrder(
// it starts by setting to IDLE
postStates.EXPECT().Set(sig.NodeID(), types.PostStateIdle),
// initial proof
postStates.EXPECT().Set(sig.NodeID(), types.PostStateProving),
postStates.EXPECT().Set(sig.NodeID(), types.PostStateIdle),
// 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),
)
tab.Register(sig)
}

Expand Down
30 changes: 6 additions & 24 deletions activation/e2e/builds_atx_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,41 +51,26 @@ func TestBuilder_SwitchesToBuildV2(t *testing.T) {
sig, err := signing.NewEdSigner()
require.NoError(t, err)

cfg := activation.DefaultPostConfig()
cfg := testPostConfig()
db := sql.InMemory()
cdb := datastore.NewCachedDB(db, logger)

syncer := activation.NewMocksyncer(ctrl)
syncer.EXPECT().RegisterForATXSynced().DoAndReturn(func() <-chan struct{} {
synced := make(chan struct{})
close(synced)
return synced
}).AnyTimes()

opts := testPostSetupOpts(t)
svc := grpcserver.NewPostService(logger)
svc.AllowConnections(true)
grpcCfg, cleanup := launchServer(t, svc)
t.Cleanup(cleanup)

initPost(t, cfg, opts, sig, goldenATX, grpcCfg, svc)

poetDb := activation.NewPoetDb(db, logger.Named("poetDb"))
verifier, err := activation.NewPostVerifier(cfg, logger.Named("verifier"))
require.NoError(t, err)
t.Cleanup(func() { assert.NoError(t, verifier.Close()) })
opts := testPostSetupOpts(t)

validator := activation.NewValidator(db, poetDb, cfg, opts.Scrypt, verifier)

atxsdata := atxsdata.New()
mgr, err := activation.NewPostSetupManager(cfg, logger, cdb, atxsdata, goldenATX, syncer, validator)
require.NoError(t, err)

initPost(t, mgr, opts, sig.NodeID())
stop := launchPostSupervisor(t, logger, mgr, sig, grpcCfg, opts)
t.Cleanup(stop)

require.Eventually(t, func() bool {
_, err := svc.Client(sig.NodeID())
return err == nil
}, 10*time.Second, 100*time.Millisecond, "timed out waiting for connection")

// ensure that genesis aligns with layer timings
genesis := time.Now().Round(layerDuration)
Expand All @@ -108,7 +93,6 @@ func TestBuilder_SwitchesToBuildV2(t *testing.T) {
client := ae2e.NewTestPoetClient(1)
poetClient := activation.NewPoetServiceWithClient(poetDb, client, poetCfg, logger)

postStates := activation.NewPostStates(logger)
localDB := localsql.InMemory()
nb, err := activation.NewNIPostBuilder(
localDB,
Expand All @@ -117,7 +101,6 @@ func TestBuilder_SwitchesToBuildV2(t *testing.T) {
poetCfg,
clock,
validator,
activation.NipostbuilderWithPostStates(postStates),
activation.WithPoetServices(poetClient),
)
require.NoError(t, err)
Expand Down Expand Up @@ -218,11 +201,10 @@ func TestBuilder_SwitchesToBuildV2(t *testing.T) {
mpub,
nb,
clock,
syncer,
syncedSyncer(t),
logger,
activation.WithPoetConfig(poetCfg),
activation.WithValidator(validator),
activation.WithPostStates(postStates),
activation.BuilderAtxVersions(atxVersions),
)
tab.Register(sig)
Expand Down
39 changes: 11 additions & 28 deletions activation/e2e/certifier_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"time"

poetShared "github.com/spacemeshos/poet/shared"
"github.com/spacemeshos/post/initialization"
"github.com/spacemeshos/post/shared"
"github.com/spacemeshos/post/verifying"
"github.com/stretchr/testify/require"
Expand All @@ -22,7 +21,6 @@ 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/signing"
"github.com/spacemeshos/go-spacemesh/sql"
Expand All @@ -31,46 +29,31 @@ import (
)

func TestCertification(t *testing.T) {
ctrl := gomock.NewController(t)
sig, err := signing.NewEdSigner()
require.NoError(t, err)

logger := zaptest.NewLogger(t)
cfg := activation.DefaultPostConfig()
cfg := testPostConfig()
db := sql.InMemory()
localDb := localsql.InMemory()

syncer := activation.NewMocksyncer(gomock.NewController(t))
synced := make(chan struct{})
close(synced)
syncer.EXPECT().RegisterForATXSynced().AnyTimes().Return(synced)
opts := testPostSetupOpts(t)
svc := grpcserver.NewPostService(zaptest.NewLogger(t))
svc.AllowConnections(true)
grpcCfg, cleanup := launchServer(t, svc)
t.Cleanup(cleanup)

initPost(t, cfg, opts, sig, types.RandomATXID(), grpcCfg, svc)

validator := activation.NewMocknipostValidator(gomock.NewController(t))
validator := activation.NewMocknipostValidator(ctrl)
validator.EXPECT().
Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
AnyTimes()
validator.EXPECT().VerifyChain(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()

mgr, err := activation.NewPostSetupManager(cfg, logger, db, atxsdata.New(), types.ATXID{2, 3, 4}, syncer, validator)
postClient, err := svc.Client(sig.NodeID())
require.NoError(t, err)

opts := activation.DefaultPostSetupOpts()
opts.DataDir = t.TempDir()
opts.ProviderID.SetUint32(initialization.CPUProviderID())
opts.Scrypt.N = 2 // Speedup initialization in tests.
initPost(t, mgr, opts, sig.NodeID())

svc := grpcserver.NewPostService(logger)
svc.AllowConnections(true)
grpcCfg, cleanup := launchServer(t, svc)
t.Cleanup(cleanup)
t.Cleanup(launchPostSupervisor(t, logger, mgr, sig, grpcCfg, opts))

var postClient activation.PostClient
require.Eventually(t, func() bool {
var err error
postClient, err = svc.Client(sig.NodeID())
return err == nil
}, 10*time.Second, 100*time.Millisecond, "timed out waiting for connection")
post, info, err := postClient.Proof(context.Background(), shared.ZeroChallenge)
require.NoError(t, err)

Expand Down
Loading

0 comments on commit 070fdc8

Please sign in to comment.