diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a072cd0f5..5ffd5163a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ See [RELEASE](./RELEASE.md) for workflow instructions. * [#5730](https://github.com/spacemeshos/go-spacemesh/pull/5730) Fixed a bug where the node behaves incorrectly when first started with supervised smeshing. +* [#5731](https://github.com/spacemeshos/go-spacemesh/pull/5731) The default listen address for `PostService` is now + `127.0.0.1:0` instead of `127.0.0.1:9094`. This will ensure that a node binds the post service to a random free port + and prevents multiple instances of the post service from binding to the same port. + ## Release v1.4.1 ### Improvements diff --git a/activation/e2e/nipost_test.go b/activation/e2e/nipost_test.go index 694920bd80..e9712b9e3e 100644 --- a/activation/e2e/nipost_test.go +++ b/activation/e2e/nipost_test.go @@ -76,10 +76,8 @@ func launchPostSupervisor( builder := activation.NewMockAtxBuilder(gomock.NewController(tb)) builder.EXPECT().Register(gomock.Any()) - ps, err := activation.NewPostSupervisor(log, cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(tb, err) - require.NotNil(tb, ps) - require.NoError(tb, ps.Start(postOpts, sig)) + ps := activation.NewPostSupervisor(log, postCfg, provingOpts, mgr, builder) + require.NoError(tb, ps.Start(cmdCfg, postOpts, sig)) return func() { assert.NoError(tb, ps.Stop(false)) } } diff --git a/activation/post_supervisor.go b/activation/post_supervisor.go index e089256739..187623972c 100644 --- a/activation/post_supervisor.go +++ b/activation/post_supervisor.go @@ -66,7 +66,6 @@ type PostSupervisorConfig struct { type PostSupervisor struct { logger *zap.Logger - cmdCfg PostSupervisorConfig postCfg PostConfig provingOpts PostProvingOpts @@ -83,25 +82,19 @@ type PostSupervisor struct { // NewPostSupervisor returns a new post service. func NewPostSupervisor( logger *zap.Logger, - cmdCfg PostSupervisorConfig, postCfg PostConfig, provingOpts PostProvingOpts, postSetupProvider postSetupProvider, atxBuilder AtxBuilder, -) (*PostSupervisor, error) { - if _, err := os.Stat(cmdCfg.PostServiceCmd); err != nil { - return nil, fmt.Errorf("post service binary not found: %s", cmdCfg.PostServiceCmd) - } - +) *PostSupervisor { return &PostSupervisor{ logger: logger, - cmdCfg: cmdCfg, postCfg: postCfg, provingOpts: provingOpts, postSetupProvider: postSetupProvider, atxBuilder: atxBuilder, - }, nil + } } func (ps *PostSupervisor) Config() PostConfig { @@ -137,13 +130,17 @@ func (ps *PostSupervisor) Status() *PostSetupStatus { return ps.postSetupProvider.Status() } -func (ps *PostSupervisor) Start(opts PostSetupOpts, sig *signing.EdSigner) error { +func (ps *PostSupervisor) Start(cmdCfg PostSupervisorConfig, opts PostSetupOpts, sig *signing.EdSigner) error { ps.mtx.Lock() defer ps.mtx.Unlock() if ps.stop != nil { return fmt.Errorf("post service already started") } + if _, err := os.Stat(cmdCfg.PostServiceCmd); err != nil { + return fmt.Errorf("stat post service binary %s: %w", cmdCfg.PostServiceCmd, err) + } + // TODO(mafa): verify that opts don't delete existing files ps.eg = errgroup.Group{} // reset errgroup to allow restarts. @@ -171,7 +168,7 @@ func (ps *PostSupervisor) Start(opts PostSetupOpts, sig *signing.EdSigner) error } ps.atxBuilder.Register(sig) - return ps.runCmd(ctx, ps.cmdCfg, ps.postCfg, opts, ps.provingOpts, sig.NodeID()) + return ps.runCmd(ctx, cmdCfg, ps.postCfg, opts, ps.provingOpts, sig.NodeID()) }) return nil } diff --git a/activation/post_supervisor_test.go b/activation/post_supervisor_test.go index 5d86b43583..8ea9d6bf27 100644 --- a/activation/post_supervisor_test.go +++ b/activation/post_supervisor_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "io/fs" "os" "path/filepath" "runtime" @@ -68,24 +69,24 @@ func Test_PostSupervisor_ErrorOnMissingBinary(t *testing.T) { cmdCfg := DefaultTestPostServiceConfig() cmdCfg.PostServiceCmd = "missing" postCfg := DefaultPostConfig() + postOpts := DefaultPostSetupOpts() provingOpts := DefaultPostProvingOpts() + sig, err := signing.NewEdSigner() + require.NoError(t, err) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, nil, nil) - require.ErrorContains(t, err, "post service binary not found") - require.Nil(t, ps) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, nil, nil) + err = ps.Start(cmdCfg, postOpts, sig) + require.ErrorIs(t, err, fs.ErrNotExist) + require.ErrorContains(t, err, "stat post service binary missing") } func Test_PostSupervisor_StopWithoutStart(t *testing.T) { log := zaptest.NewLogger(t) - cmdCfg := DefaultTestPostServiceConfig() postCfg := DefaultPostConfig() provingOpts := DefaultPostProvingOpts() - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, nil, nil) - require.NoError(t, err) - require.NotNil(t, ps) - + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, nil, nil) require.NoError(t, ps.Stop(false)) } @@ -104,11 +105,9 @@ func Test_PostSupervisor_Start_FailPrepare(t *testing.T) { testErr := errors.New("test error") mgr.EXPECT().PrepareInitializer(gomock.Any(), postOpts, sig.NodeID()).Return(testErr) builder := NewMockAtxBuilder(ctrl) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) - require.NotNil(t, ps) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) - require.NoError(t, ps.Start(postOpts, sig)) + require.NoError(t, ps.Start(cmdCfg, postOpts, sig)) require.ErrorIs(t, ps.Stop(false), testErr) } @@ -141,11 +140,9 @@ func Test_PostSupervisor_Start_FailStartSession(t *testing.T) { mgr.EXPECT().PrepareInitializer(gomock.Any(), postOpts, sig.NodeID()).Return(nil) mgr.EXPECT().StartSession(gomock.Any(), sig.NodeID()).Return(errors.New("failed start session")) builder := NewMockAtxBuilder(ctrl) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) - require.NotNil(t, ps) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) - require.NoError(t, ps.Start(postOpts, sig)) + require.NoError(t, ps.Start(cmdCfg, postOpts, sig)) require.EqualError(t, ps.eg.Wait(), "failed start session") } @@ -163,11 +160,9 @@ func Test_PostSupervisor_StartsServiceCmd(t *testing.T) { mgr := newPostManager(t, postCfg, postOpts) builder := NewMockAtxBuilder(ctrl) builder.EXPECT().Register(sig) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) - require.NotNil(t, ps) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) - require.NoError(t, ps.Start(postOpts, sig)) + require.NoError(t, ps.Start(cmdCfg, postOpts, sig)) t.Cleanup(func() { assert.NoError(t, ps.Stop(false)) }) require.Eventually(t, func() bool { return ps.pid.Load() != 0 }, 5*time.Second, 100*time.Millisecond) @@ -202,11 +197,9 @@ func Test_PostSupervisor_Restart_Possible(t *testing.T) { mgr := newPostManager(t, postCfg, postOpts) builder := NewMockAtxBuilder(ctrl) builder.EXPECT().Register(sig) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) - require.NotNil(t, ps) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) - require.NoError(t, ps.Start(postOpts, sig)) + require.NoError(t, ps.Start(cmdCfg, postOpts, sig)) t.Cleanup(func() { assert.NoError(t, ps.Stop(false)) }) require.Eventually(t, func() bool { return ps.pid.Load() != 0 }, 5*time.Second, 100*time.Millisecond) @@ -214,7 +207,7 @@ func Test_PostSupervisor_Restart_Possible(t *testing.T) { require.Eventually(t, func() bool { return ps.pid.Load() == 0 }, 5*time.Second, 100*time.Millisecond) builder.EXPECT().Register(sig) - require.NoError(t, ps.Start(postOpts, sig)) + require.NoError(t, ps.Start(cmdCfg, postOpts, sig)) require.Eventually(t, func() bool { return ps.pid.Load() != 0 }, 5*time.Second, 100*time.Millisecond) require.NoError(t, ps.Stop(false)) @@ -235,11 +228,9 @@ func Test_PostSupervisor_LogFatalOnCrash(t *testing.T) { mgr := newPostManager(t, postCfg, postOpts) builder := NewMockAtxBuilder(ctrl) builder.EXPECT().Register(sig) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) - require.NotNil(t, ps) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) - require.NoError(t, ps.Start(postOpts, sig)) + require.NoError(t, ps.Start(cmdCfg, postOpts, sig)) t.Cleanup(func() { assert.NoError(t, ps.Stop(false)) }) require.Eventually(t, func() bool { return ps.pid.Load() != 0 }, 5*time.Second, 100*time.Millisecond) @@ -270,11 +261,9 @@ func Test_PostSupervisor_LogFatalOnInvalidConfig(t *testing.T) { mgr := newPostManager(t, postCfg, postOpts) builder := NewMockAtxBuilder(ctrl) builder.EXPECT().Register(sig) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) - require.NotNil(t, ps) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) - require.NoError(t, ps.Start(postOpts, sig)) + require.NoError(t, ps.Start(cmdCfg, postOpts, sig)) t.Cleanup(func() { assert.NoError(t, ps.Stop(false)) }) require.Eventually(t, func() bool { return ps.pid.Load() != 0 }, 5*time.Second, 100*time.Millisecond) @@ -312,11 +301,9 @@ func Test_PostSupervisor_StopOnError(t *testing.T) { }) builder := NewMockAtxBuilder(ctrl) builder.EXPECT().Register(sig) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) - require.NotNil(t, ps) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) - require.NoError(t, ps.Start(postOpts, sig)) + require.NoError(t, ps.Start(cmdCfg, postOpts, sig)) t.Cleanup(func() { assert.NoError(t, ps.Stop(false)) }) require.Eventually(t, func() bool { return ps.pid.Load() != 0 }, 5*time.Second, 100*time.Millisecond) @@ -328,15 +315,13 @@ func Test_PostSupervisor_StopOnError(t *testing.T) { func Test_PostSupervisor_Providers_includesCPU(t *testing.T) { log := zaptest.NewLogger(t) - cmdCfg := DefaultTestPostServiceConfig() postCfg := DefaultPostConfig() provingOpts := DefaultPostProvingOpts() ctrl := gomock.NewController(t) mgr := NewMockpostSetupProvider(ctrl) builder := NewMockAtxBuilder(ctrl) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) providers, err := ps.Providers() require.NoError(t, err) @@ -352,15 +337,13 @@ func Test_PostSupervisor_Providers_includesCPU(t *testing.T) { func Test_PostSupervisor_Benchmark(t *testing.T) { log := zaptest.NewLogger(t) - cmdCfg := DefaultTestPostServiceConfig() postCfg := DefaultPostConfig() provingOpts := DefaultPostProvingOpts() ctrl := gomock.NewController(t) mgr := NewMockpostSetupProvider(ctrl) builder := NewMockAtxBuilder(ctrl) - ps, err := NewPostSupervisor(log.Named("supervisor"), cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(t, err) + ps := NewPostSupervisor(log.Named("supervisor"), postCfg, provingOpts, mgr, builder) providers, err := ps.Providers() require.NoError(t, err) diff --git a/api/grpcserver/config.go b/api/grpcserver/config.go index 54573565d1..56ed717887 100644 --- a/api/grpcserver/config.go +++ b/api/grpcserver/config.go @@ -54,7 +54,7 @@ func DefaultConfig() Config { PrivateServices: []Service{Admin, Smesher, Debug, ActivationStreamV2Alpha1, RewardStreamV2Alpha1}, PrivateListener: "127.0.0.1:9093", PostServices: []Service{Post, PostInfo}, - PostListener: "127.0.0.1:9094", + PostListener: "127.0.0.1:0", TLSServices: []Service{Post, PostInfo}, TLSListener: "", JSONListener: "", diff --git a/api/grpcserver/grpc.go b/api/grpcserver/grpc.go index 7cb21d8af7..3a90cab455 100644 --- a/api/grpcserver/grpc.go +++ b/api/grpcserver/grpc.go @@ -79,6 +79,8 @@ func NewWithServices( ip := net.ParseIP(host) if host != "localhost" && !ip.IsPrivate() && !ip.IsLoopback() { logger.Warn("unsecured grpc server is listening on a public IP address", zap.String("address", listener)) + } else { + logger.Info("grpc server is listening on a private IP address", zap.String("address", listener)) } server := New(listener, logger, config, grpcOpts...) @@ -170,6 +172,7 @@ func (s *Server) Start() error { } s.BoundAddress = lis.Addr().String() reflection.Register(s.GrpcServer) + s.logger.Info("bound to address", zap.String("address", s.BoundAddress)) s.grp.Go(func() error { if err := s.GrpcServer.Serve(lis); err != nil { s.logger.Error("serving grpc server", zap.Error(err)) diff --git a/api/grpcserver/grpcserver_test.go b/api/grpcserver/grpcserver_test.go index 3030a35569..a5db13ed3a 100644 --- a/api/grpcserver/grpcserver_test.go +++ b/api/grpcserver/grpcserver_test.go @@ -562,9 +562,10 @@ func setupSmesherService(t *testing.T, sig *signing.EdSigner) (*smesherServiceCo smeshingProvider, postSupervisor, 10*time.Millisecond, - sig, activation.DefaultPostSetupOpts(), + sig, ) + svc.SetPostServiceConfig(activation.DefaultTestPostServiceConfig()) cfg, cleanup := launchServer(t, svc) t.Cleanup(cleanup) @@ -584,7 +585,11 @@ func setupSmesherService(t *testing.T, sig *signing.EdSigner) (*smesherServiceCo func TestSmesherService(t *testing.T) { t.Run("IsSmeshing", func(t *testing.T) { t.Parallel() - c, ctx := setupSmesherService(t, nil) + + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + c, ctx := setupSmesherService(t, sig) c.smeshingProvider.EXPECT().Smeshing().Return(false) res, err := c.IsSmeshing(ctx, &emptypb.Empty{}) require.NoError(t, err) @@ -593,8 +598,12 @@ func TestSmesherService(t *testing.T) { t.Run("StartSmeshingMissingArgs", func(t *testing.T) { t.Parallel() - c, ctx := setupSmesherService(t, nil) - _, err := c.StartSmeshing(ctx, &pb.StartSmeshingRequest{}) + + sig, err := signing.NewEdSigner() + require.NoError(t, err) + + c, ctx := setupSmesherService(t, sig) + _, err = c.StartSmeshing(ctx, &pb.StartSmeshingRequest{}) require.Equal(t, codes.InvalidArgument, status.Code(err)) }) @@ -611,15 +620,16 @@ func TestSmesherService(t *testing.T) { c, ctx := setupSmesherService(t, sig) c.smeshingProvider.EXPECT().StartSmeshing(gomock.Any()).Return(nil) - c.postSupervisor.EXPECT().Start(gomock.All( - gomock.Cond(func(postOpts any) bool { return postOpts.(activation.PostSetupOpts).DataDir == opts.DataDir }), - gomock.Cond( - func(postOpts any) bool { return postOpts.(activation.PostSetupOpts).NumUnits == opts.NumUnits }, - ), - gomock.Cond( - func(postOpts any) bool { return postOpts.(activation.PostSetupOpts).MaxFileSize == opts.MaxFileSize }, - ), - ), sig).Return(nil) + c.postSupervisor.EXPECT().Start(gomock.Any(), + gomock.All( + gomock.Cond(func(postOpts any) bool { return postOpts.(activation.PostSetupOpts).DataDir == opts.DataDir }), + gomock.Cond( + func(postOpts any) bool { return postOpts.(activation.PostSetupOpts).NumUnits == opts.NumUnits }, + ), + gomock.Cond( + func(postOpts any) bool { return postOpts.(activation.PostSetupOpts).MaxFileSize == opts.MaxFileSize }, + ), + ), sig).Return(nil) res, err := c.StartSmeshing(ctx, &pb.StartSmeshingRequest{ Opts: opts, Coinbase: coinbase, diff --git a/api/grpcserver/interface.go b/api/grpcserver/interface.go index e31f24fe11..aa8041c3c8 100644 --- a/api/grpcserver/interface.go +++ b/api/grpcserver/interface.go @@ -63,7 +63,7 @@ type postState interface { } type postSupervisor interface { - Start(opts activation.PostSetupOpts, sig *signing.EdSigner) error + Start(cmdCfg activation.PostSupervisorConfig, opts activation.PostSetupOpts, sig *signing.EdSigner) error Stop(deleteFiles bool) error Config() activation.PostConfig diff --git a/api/grpcserver/mocks.go b/api/grpcserver/mocks.go index c00753a69d..8a5b9a6d97 100644 --- a/api/grpcserver/mocks.go +++ b/api/grpcserver/mocks.go @@ -1151,17 +1151,17 @@ func (c *MockpostSupervisorProvidersCall) DoAndReturn(f func() ([]activation.Pos } // Start mocks base method. -func (m *MockpostSupervisor) Start(opts activation.PostSetupOpts, sig *signing.EdSigner) error { +func (m *MockpostSupervisor) Start(cmdCfg activation.PostSupervisorConfig, opts activation.PostSetupOpts, sig *signing.EdSigner) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Start", opts, sig) + ret := m.ctrl.Call(m, "Start", cmdCfg, opts, sig) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. -func (mr *MockpostSupervisorMockRecorder) Start(opts, sig any) *MockpostSupervisorStartCall { +func (mr *MockpostSupervisorMockRecorder) Start(cmdCfg, opts, sig any) *MockpostSupervisorStartCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockpostSupervisor)(nil).Start), opts, sig) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockpostSupervisor)(nil).Start), cmdCfg, opts, sig) return &MockpostSupervisorStartCall{Call: call} } @@ -1177,13 +1177,13 @@ func (c *MockpostSupervisorStartCall) Return(arg0 error) *MockpostSupervisorStar } // Do rewrite *gomock.Call.Do -func (c *MockpostSupervisorStartCall) Do(f func(activation.PostSetupOpts, *signing.EdSigner) error) *MockpostSupervisorStartCall { +func (c *MockpostSupervisorStartCall) Do(f func(activation.PostSupervisorConfig, activation.PostSetupOpts, *signing.EdSigner) error) *MockpostSupervisorStartCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockpostSupervisorStartCall) DoAndReturn(f func(activation.PostSetupOpts, *signing.EdSigner) error) *MockpostSupervisorStartCall { +func (c *MockpostSupervisorStartCall) DoAndReturn(f func(activation.PostSupervisorConfig, activation.PostSetupOpts, *signing.EdSigner) error) *MockpostSupervisorStartCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/api/grpcserver/post_service_test.go b/api/grpcserver/post_service_test.go index 3a47b27c5e..41ceb61f2d 100644 --- a/api/grpcserver/post_service_test.go +++ b/api/grpcserver/post_service_test.go @@ -61,10 +61,8 @@ func launchPostSupervisor( // start post supervisor builder := activation.NewMockAtxBuilder(ctrl) builder.EXPECT().Register(sig) - ps, err := activation.NewPostSupervisor(log, serviceCfg, postCfg, provingOpts, mgr, builder) - require.NoError(tb, err) - require.NotNil(tb, ps) - require.NoError(tb, ps.Start(postOpts, sig)) + ps := activation.NewPostSupervisor(log, postCfg, provingOpts, mgr, builder) + require.NoError(tb, ps.Start(serviceCfg, postOpts, sig)) return sig.NodeID(), func() { assert.NoError(tb, ps.Stop(false)) } } @@ -106,10 +104,8 @@ func launchPostSupervisorTLS( // start post supervisor builder := activation.NewMockAtxBuilder(ctrl) builder.EXPECT().Register(sig) - ps, err := activation.NewPostSupervisor(log, serviceCfg, postCfg, provingOpts, mgr, builder) - require.NoError(tb, err) - require.NotNil(tb, ps) - require.NoError(tb, ps.Start(postOpts, sig)) + ps := activation.NewPostSupervisor(log, postCfg, provingOpts, mgr, builder) + require.NoError(tb, ps.Start(serviceCfg, postOpts, sig)) return sig.NodeID(), func() { assert.NoError(tb, ps.Stop(false)) } } diff --git a/api/grpcserver/smesher_service.go b/api/grpcserver/smesher_service.go index 64030021d1..c45c4838ea 100644 --- a/api/grpcserver/smesher_service.go +++ b/api/grpcserver/smesher_service.go @@ -28,8 +28,9 @@ type SmesherService struct { postSupervisor postSupervisor streamInterval time.Duration - sig *signing.EdSigner + cmdCfg *activation.PostSupervisorConfig postOpts activation.PostSetupOpts + sig *signing.EdSigner } // RegisterService registers this service with a grpc server instance. @@ -51,20 +52,28 @@ func NewSmesherService( smeshing activation.SmeshingProvider, postSupervisor postSupervisor, streamInterval time.Duration, - sig *signing.EdSigner, postOpts activation.PostSetupOpts, + sig *signing.EdSigner, ) *SmesherService { return &SmesherService{ smeshingProvider: smeshing, postSupervisor: postSupervisor, streamInterval: streamInterval, - sig: sig, postOpts: postOpts, + sig: sig, } } +// SetPostServiceConfig sets the post supervisor config. +func (s *SmesherService) SetPostServiceConfig(cfg activation.PostSupervisorConfig) { + s.cmdCfg = &cfg +} + // IsSmeshing reports whether the node is smeshing. func (s SmesherService) IsSmeshing(context.Context, *emptypb.Empty) (*pb.IsSmeshingResponse, error) { + if s.sig == nil { + return nil, status.Errorf(codes.FailedPrecondition, "node is not configured for supervised smeshing") + } return &pb.IsSmeshingResponse{IsSmeshing: s.smeshingProvider.Smeshing()}, nil } @@ -73,6 +82,17 @@ func (s SmesherService) StartSmeshing( ctx context.Context, in *pb.StartSmeshingRequest, ) (*pb.StartSmeshingResponse, error) { + if s.sig == nil { + return nil, status.Errorf(codes.FailedPrecondition, "node is not configured for supervised smeshing") + } + if s.cmdCfg == nil { + return nil, status.Errorf(codes.FailedPrecondition, "post supervisor config is not set") + } + opts, err := s.postSetupOpts(in.Opts) + if err != nil { + status.Error(codes.InvalidArgument, err.Error()) + } + if in.Coinbase == nil { return nil, status.Errorf(codes.InvalidArgument, "`Coinbase` must be provided") } @@ -81,14 +101,7 @@ func (s SmesherService) StartSmeshing( return nil, fmt.Errorf("failed to parse in.Coinbase.Address `%s`: %w", in.Coinbase.Address, err) } - opts, err := s.postSetupOpts(in.Opts) - if err != nil { - status.Error(codes.InvalidArgument, err.Error()) - } - if s.sig == nil { - return nil, status.Errorf(codes.FailedPrecondition, "node is not configured for supervised smeshing") - } - if err := s.postSupervisor.Start(opts, s.sig); err != nil { + if err := s.postSupervisor.Start(*s.cmdCfg, opts, s.sig); err != nil { ctxzap.Error(ctx, "failed to start post supervisor", zap.Error(err)) return nil, status.Error(codes.Internal, fmt.Sprintf("failed to start post supervisor: %v", err)) } diff --git a/api/grpcserver/smesher_service_test.go b/api/grpcserver/smesher_service_test.go index 71d038f530..aff6e63de4 100644 --- a/api/grpcserver/smesher_service_test.go +++ b/api/grpcserver/smesher_service_test.go @@ -28,8 +28,8 @@ func TestPostConfig(t *testing.T) { smeshingProvider, postSupervisor, time.Second, - nil, activation.DefaultPostSetupOpts(), + nil, ) postConfig := activation.PostConfig{ @@ -57,13 +57,15 @@ func TestStartSmeshingPassesCorrectSmeshingOpts(t *testing.T) { postSupervisor := grpcserver.NewMockpostSupervisor(ctrl) sig, err := signing.NewEdSigner() require.NoError(t, err) + cmdCfg := activation.DefaultTestPostServiceConfig() svc := grpcserver.NewSmesherService( smeshingProvider, postSupervisor, time.Second, - sig, activation.DefaultPostSetupOpts(), + sig, ) + svc.SetPostServiceConfig(cmdCfg) types.SetNetworkHRP("stest") addr, err := types.StringToAddress("stest1qqqqqqrs60l66w5uksxzmaznwq6xnhqfv56c28qlkm4a5") @@ -78,7 +80,7 @@ func TestStartSmeshingPassesCorrectSmeshingOpts(t *testing.T) { ComputeBatchSize: config.DefaultComputeBatchSize, } opts.ProviderID.SetUint32(providerID) - postSupervisor.EXPECT().Start(opts, sig).Return(nil) + postSupervisor.EXPECT().Start(cmdCfg, opts, sig).Return(nil) smeshingProvider.EXPECT().StartSmeshing(addr).Return(nil) _, err = svc.StartSmeshing(context.Background(), &pb.StartSmeshingRequest{ @@ -94,6 +96,45 @@ func TestStartSmeshingPassesCorrectSmeshingOpts(t *testing.T) { require.NoError(t, err) } +func TestStartSmeshing_ErrorOnMissingPostServiceConfig(t *testing.T) { + ctrl := gomock.NewController(t) + smeshingProvider := activation.NewMockSmeshingProvider(ctrl) + postSupervisor := grpcserver.NewMockpostSupervisor(ctrl) + sig, err := signing.NewEdSigner() + require.NoError(t, err) + svc := grpcserver.NewSmesherService( + smeshingProvider, + postSupervisor, + time.Second, + activation.DefaultPostSetupOpts(), + sig, + ) + + providerID := uint32(7) + opts := activation.PostSetupOpts{ + DataDir: "data-dir", + NumUnits: 1, + MaxFileSize: 1024, + Throttle: true, + Scrypt: config.DefaultLabelParams(), + ComputeBatchSize: config.DefaultComputeBatchSize, + } + opts.ProviderID.SetUint32(providerID) + + _, err = svc.StartSmeshing(context.Background(), &pb.StartSmeshingRequest{ + Coinbase: &pb.AccountId{Address: "stest1qqqqqqrs60l66w5uksxzmaznwq6xnhqfv56c28qlkm4a5"}, + Opts: &pb.PostSetupOpts{ + DataDir: "data-dir", + NumUnits: 1, + MaxFileSize: 1024, + ProviderId: &providerID, + Throttle: true, + }, + }) + require.Equal(t, codes.FailedPrecondition, status.Code(err)) + require.ErrorContains(t, err, "post supervisor config is not set") +} + func TestStartSmeshing_ErrorOnMultiSmeshingSetup(t *testing.T) { ctrl := gomock.NewController(t) smeshingProvider := activation.NewMockSmeshingProvider(ctrl) @@ -102,9 +143,10 @@ func TestStartSmeshing_ErrorOnMultiSmeshingSetup(t *testing.T) { smeshingProvider, postSupervisor, time.Second, - nil, // no nodeID in multi smesher setup activation.DefaultPostSetupOpts(), + nil, // no nodeID in multi smesher setup ) + svc.SetPostServiceConfig(activation.DefaultTestPostServiceConfig()) types.SetNetworkHRP("stest") providerID := uint32(7) @@ -140,8 +182,8 @@ func TestSmesherService_PostSetupProviders(t *testing.T) { smeshingProvider, postSupervisor, time.Second, - nil, activation.DefaultPostSetupOpts(), + nil, // no nodeID in multi smesher setup ) providers := []activation.PostSetupProvider{ @@ -187,8 +229,8 @@ func TestSmesherService_PostSetupStatus(t *testing.T) { smeshingProvider, postSupervisor, time.Second, - nil, activation.DefaultPostSetupOpts(), + nil, ) postSupervisor.EXPECT().Status().Return(&activation.PostSetupStatus{ @@ -211,8 +253,8 @@ func TestSmesherService_PostSetupStatus(t *testing.T) { smeshingProvider, postSupervisor, time.Second, - nil, activation.DefaultPostSetupOpts(), + nil, ) id := activation.PostProviderID{} @@ -248,8 +290,8 @@ func TestSmesherService_PostSetupStatus(t *testing.T) { smeshingProvider, postSupervisor, time.Second, - nil, activation.DefaultPostSetupOpts(), + nil, ) id := activation.PostProviderID{} @@ -286,8 +328,8 @@ func TestSmesherService_SmesherID(t *testing.T) { smeshingProvider, postSupervisor, time.Second, - nil, activation.DefaultPostSetupOpts(), + nil, ) resp, err := svc.SmesherID(context.Background(), &emptypb.Empty{}) diff --git a/config/presets/standalone.go b/config/presets/standalone.go index d3e6626365..51a55bb837 100644 --- a/config/presets/standalone.go +++ b/config/presets/standalone.go @@ -88,7 +88,7 @@ func standalone() config.Config { conf.API.PublicListener = "0.0.0.0:10092" conf.API.PrivateListener = "127.0.0.1:10093" - conf.API.PostListener = "127.0.0.1:10094" + conf.API.PostListener = "127.0.0.1:0" addr, _ := multiaddr.NewMultiaddr("/ip4/0.0.0.0/tcp/17513") conf.P2P.Listen = []multiaddr.Multiaddr{addr} diff --git a/node/node.go b/node/node.go index 8ddcde8009..105c359eec 100644 --- a/node/node.go +++ b/node/node.go @@ -968,16 +968,6 @@ func (app *App) initServices(ctx context.Context) error { proposalBuilder.Register(sig) } - host, port, err := net.SplitHostPort(app.Config.API.PostListener) - if err != nil { - return fmt.Errorf("parse grpc-private-listener: %w", err) - } - ip := net.ParseIP(host) - if ip.IsUnspecified() { - host = "127.0.0.1" - } - - app.Config.POSTService.NodeAddress = fmt.Sprintf("http://%s:%s", host, port) postSetupMgr, err := activation.NewPostSetupManager( app.Config.POST, app.addLogger(PostLogger, lg).Zap(), @@ -1044,9 +1034,8 @@ func (app *App) initServices(ctx context.Context) error { atxBuilder.Register(sig) } } - app.postSupervisor, err = activation.NewPostSupervisor( + app.postSupervisor = activation.NewPostSupervisor( app.log.Zap(), - app.Config.POSTService, app.Config.POST, app.Config.SMESHING.ProvingOpts, postSetupMgr, @@ -1350,24 +1339,6 @@ func (app *App) startServices(ctx context.Context) error { } } - if app.Config.SMESHING.Start { - if app.Config.SMESHING.CoinbaseAccount == "" { - return fmt.Errorf("smeshing enabled but no coinbase account provided") - } - if len(app.signers) > 1 { - return fmt.Errorf("supervised smeshing cannot be started in a multi-smeshing setup") - } - if err := app.postSupervisor.Start( - app.Config.SMESHING.Opts, - app.signers[0], - ); err != nil { - return fmt.Errorf("start post service: %w", err) - } - } else if len(app.signers) == 1 && app.signers[0].Name() == supervisedIDKeyFileName { - // supervised setup but not started - app.log.Info("smeshing not started, waiting to be triggered via smesher api") - } - if app.ptimesync != nil { app.ptimesync.Start() } @@ -1431,8 +1402,8 @@ func (app *App) grpcService(svc grpcserver.Service, lg log.Log) (grpcserver.Serv app.atxBuilder, app.postSupervisor, app.Config.API.SmesherStreamInterval, - sig, app.Config.SMESHING.Opts, + sig, ) app.grpcServices[svc] = service return service, nil @@ -1589,6 +1560,38 @@ func (app *App) startAPIServices(ctx context.Context) error { if err := app.grpcPostServer.Start(); err != nil { return err } + host, port, err := net.SplitHostPort(app.grpcPostServer.BoundAddress) + if err != nil { + return fmt.Errorf("parse grpc-private-listener: %w", err) + } + ip := net.ParseIP(host) + if ip.IsUnspecified() { // 0.0.0.0 isn't a valid address to connect to on windows + host = "127.0.0.1" + } + app.Config.POSTService.NodeAddress = fmt.Sprintf("http://%s:%s", host, port) + svc, err := app.grpcService(grpcserver.Smesher, app.log) + if err != nil { + return err + } + svc.(*grpcserver.SmesherService).SetPostServiceConfig(app.Config.POSTService) + if app.Config.SMESHING.Start { + if app.Config.SMESHING.CoinbaseAccount == "" { + return fmt.Errorf("smeshing enabled but no coinbase account provided") + } + if len(app.signers) > 1 { + return fmt.Errorf("supervised smeshing cannot be started in a multi-smeshing setup") + } + if err := app.postSupervisor.Start( + app.Config.POSTService, + app.Config.SMESHING.Opts, + app.signers[0], + ); err != nil { + return fmt.Errorf("start post service: %w", err) + } + } else if len(app.signers) == 1 && app.signers[0].Name() == supervisedIDKeyFileName { + // supervised setup but not started + app.log.Info("smeshing not started, waiting to be triggered via smesher api") + } } if len(authenticatedSvcs) > 0 && app.Config.API.TLSListener != "" { diff --git a/node/node_test.go b/node/node_test.go index 4fbc5fbea9..4eefb24c60 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -513,7 +513,7 @@ func TestSpacemeshApp_TransactionService(t *testing.T) { // Run the app in a goroutine. As noted above, it blocks if it succeeds. // If there's an error in the args, it will return immediately. - wg := sync.WaitGroup{} + var wg sync.WaitGroup wg.Add(1) go func() { str, err := testArgs(ctx, cmdWithRun(run)) @@ -555,7 +555,7 @@ func TestSpacemeshApp_TransactionService(t *testing.T) { require.NoError(t, err) // TODO(dshulyak) synchronization below is messed up - wg2 := sync.WaitGroup{} + var wg2 sync.WaitGroup wg2.Add(1) go func() { defer wg2.Done() @@ -1036,6 +1036,7 @@ func TestAdminEvents(t *testing.T) { cfg.FileLock = filepath.Join(cfg.DataDirParent, "LOCK") cfg.SMESHING.Opts.DataDir = t.TempDir() cfg.SMESHING.Opts.Scrypt.N = 2 + cfg.SMESHING.Start = true cfg.POSTService.PostServiceCmd = activation.DefaultTestPostServiceConfig().PostServiceCmd cfg.Genesis.GenesisTime = time.Now().Add(5 * time.Second).Format(time.RFC3339) @@ -1043,9 +1044,9 @@ func TestAdminEvents(t *testing.T) { logger := logtest.New(t, zapcore.DebugLevel) app := New(WithConfig(&cfg), WithLog(logger)) - - require.NoError(t, app.NewIdentity()) require.NoError(t, app.Initialize()) + require.NoError(t, app.NewIdentity()) + ctx, cancel := context.WithCancel(context.Background()) defer cancel() var eg errgroup.Group @@ -1110,6 +1111,7 @@ func TestAdminEvents_MultiSmesher(t *testing.T) { cfg.FileLock = filepath.Join(cfg.DataDirParent, "LOCK") cfg.SMESHING.Opts.Scrypt.N = 2 cfg.SMESHING.Start = false + cfg.API.PostListener = "0.0.0.0:10094" cfg.POSTService.PostServiceCmd = activation.DefaultTestPostServiceConfig().PostServiceCmd cfg.Genesis.GenesisTime = time.Now().Add(5 * time.Second).Format(time.RFC3339) @@ -1166,7 +1168,7 @@ func TestAdminEvents_MultiSmesher(t *testing.T) { logger.Zap(), mgr, signer, - cfg.API.PostListener, + "127.0.0.1:10094", cfg.POST, cfg.SMESHING.Opts, )) @@ -1315,10 +1317,8 @@ func launchPostSupervisor( builder := activation.NewMockAtxBuilder(gomock.NewController(tb)) builder.EXPECT().Register(sig) - ps, err := activation.NewPostSupervisor(log, cmdCfg, postCfg, provingOpts, mgr, builder) - require.NoError(tb, err) - require.NotNil(tb, ps) - require.NoError(tb, ps.Start(postOpts, sig)) + ps := activation.NewPostSupervisor(log, postCfg, provingOpts, mgr, builder) + require.NoError(tb, ps.Start(cmdCfg, postOpts, sig)) return func() { assert.NoError(tb, ps.Stop(false)) } } @@ -1342,7 +1342,7 @@ func getTestDefaultConfig(tb testing.TB) *config.Config { cfg.POST.K2 = 4 cfg.SMESHING = config.DefaultSmeshingConfig() - cfg.SMESHING.Start = true + cfg.SMESHING.Start = false cfg.SMESHING.CoinbaseAccount = types.GenerateAddress([]byte{1}).String() cfg.SMESHING.Opts.DataDir = filepath.Join(tmp, "post") cfg.SMESHING.Opts.NumUnits = cfg.POST.MinNumUnits + 1 diff --git a/systest/tests/distributed_post_verification_test.go b/systest/tests/distributed_post_verification_test.go index 51de807a36..acf38a1860 100644 --- a/systest/tests/distributed_post_verification_test.go +++ b/systest/tests/distributed_post_verification_test.go @@ -129,16 +129,14 @@ func TestPostMalfeasanceProof(t *testing.T) { builder := activation.NewMockAtxBuilder(ctrl) builder.EXPECT().Register(signer) - postSupervisor, err := activation.NewPostSupervisor( + postSupervisor := activation.NewPostSupervisor( logger.Named("post-supervisor"), - cfg.POSTService, cfg.POST, cfg.SMESHING.ProvingOpts, postSetupMgr, builder, ) - require.NoError(t, err) - require.NoError(t, postSupervisor.Start(cfg.SMESHING.Opts, signer)) + require.NoError(t, postSupervisor.Start(cfg.POSTService, cfg.SMESHING.Opts, signer)) t.Cleanup(func() { assert.NoError(t, postSupervisor.Stop(false)) }) // 2. create ATX with invalid POST labels