From c3072887198724f38a83999bc673632b4089a0d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20R=C3=B3=C5=BCa=C5=84ski?= Date: Wed, 31 Jan 2024 12:23:06 +0000 Subject: [PATCH] Flag disable post verification (#5517) There are setups with one _public_ node that "talks to the Internet" and stands in front of more _private_ nodes that only connect to that _public_ node. In such a setup, the private nodes can trust that the public one already verified all data coming from the network and it is not required to repeat that work. The PoST proofs of ATXs are especially heavy. --- CHANGELOG.md | 7 ++ activation/handler.go | 3 - activation/handler_test.go | 1 - activation/post.go | 5 ++ activation/post_verifier.go | 103 +++++++++++++++++------ activation/post_verifier_scaling_test.go | 2 +- activation/post_verifier_test.go | 35 +++++--- checkpoint/recovery_test.go | 1 - cmd/root.go | 7 ++ node/node.go | 26 ++---- 10 files changed, 127 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ae522121c..4fe35b361f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ See [RELEASE](./RELEASE.md) for workflow instructions. ## Release v1.3.8 +### Features + +* [#5517](https://github.com/spacemeshos/go-spacemesh/pull/5517) + Added a flag `--smeshing-opts-verifying-disable` and a config parameter `smeshing-opts-verifying-disable` + meant for disabling verifying POST in incoming ATXs on private nodes. + The verification should be performed by the public node instead. + ### Improvements * [#5441](https://github.com/spacemeshos/go-spacemesh/pull/5441) diff --git a/activation/handler.go b/activation/handler.go index 4c7b1f2b7b..54edf52819 100644 --- a/activation/handler.go +++ b/activation/handler.go @@ -49,7 +49,6 @@ type Handler struct { log log.Log mu sync.Mutex fetcher system.Fetcher - poetCfg PoetConfig signerMtx sync.Mutex signers map[types.NodeID]*signing.EdSigner @@ -75,7 +74,6 @@ func NewHandler( beacon AtxReceiver, tortoise system.Tortoise, log log.Log, - poetCfg PoetConfig, ) *Handler { return &Handler{ local: local, @@ -91,7 +89,6 @@ func NewHandler( fetcher: fetcher, beacon: beacon, tortoise: tortoise, - poetCfg: poetCfg, signers: make(map[types.NodeID]*signing.EdSigner), inProgress: make(map[types.ATXID][]chan error), diff --git a/activation/handler_test.go b/activation/handler_test.go index a8a4a68ca1..f8b640eba4 100644 --- a/activation/handler_test.go +++ b/activation/handler_test.go @@ -117,7 +117,6 @@ func newTestHandler(tb testing.TB, goldenATXID types.ATXID) *testHandler { mbeacon, mtortoise, lg, - PoetConfig{}, ) return &testHandler{ Handler: atxHdlr, diff --git a/activation/post.go b/activation/post.go index c3a29446c4..ddf71c0ecb 100644 --- a/activation/post.go +++ b/activation/post.go @@ -79,6 +79,11 @@ func DefaultPostProvingOpts() PostProvingOpts { // PostProofVerifyingOpts are the options controlling POST proving process. type PostProofVerifyingOpts struct { + // Disable verifying POST proofs. Experimental. + // Use with caution, only on private nodes with a trusted public peer that + // validates the proofs. + Disabled bool `mapstructure:"smeshing-opts-verifying-disable"` + // Number of workers spawned to verify proofs. Workers int `mapstructure:"smeshing-opts-verifying-workers"` // The minimum number of verifying workers to keep diff --git a/activation/post_verifier.go b/activation/post_verifier.go index fbe8e7361b..4572a94d1f 100644 --- a/activation/post_verifier.go +++ b/activation/post_verifier.go @@ -60,7 +60,7 @@ func (a autoscaler) run(stop chan struct{}, s scaler, min, target int) { } } -type OffloadingPostVerifier struct { +type offloadingPostVerifier struct { eg errgroup.Group log *zap.Logger verifier PostVerifier @@ -97,40 +97,79 @@ func (v *postVerifier) Verify( return v.ProofVerifier.Verify(p, m, v.cfg, v.logger, opts...) } -// NewPostVerifier creates a new post verifier. -func NewPostVerifier(cfg PostConfig, logger *zap.Logger, opts ...verifying.OptionFunc) (PostVerifier, error) { - verifier, err := verifying.NewProofVerifier(opts...) - if err != nil { - return nil, err +type postVerifierOpts struct { + opts PostProofVerifyingOpts + prioritizedIds []types.NodeID + autoscaling bool +} + +type PostVerifierOpt func(v *postVerifierOpts) + +func WithVerifyingOpts(opts PostProofVerifyingOpts) PostVerifierOpt { + return func(v *postVerifierOpts) { + v.opts = opts } +} - return &postVerifier{logger: logger, ProofVerifier: verifier, cfg: cfg.ToConfig()}, nil +func PrioritizedIDs(ids ...types.NodeID) PostVerifierOpt { + return func(v *postVerifierOpts) { + v.prioritizedIds = ids + } } -type OffloadingPostVerifierOpt func(v *OffloadingPostVerifier) +func WithAutoscaling() PostVerifierOpt { + return func(v *postVerifierOpts) { + v.autoscaling = true + } +} -func PrioritizedIDs(ids ...types.NodeID) OffloadingPostVerifierOpt { - return func(v *OffloadingPostVerifier) { - for _, id := range ids { - v.prioritizedIds[id] = struct{}{} - } +// NewPostVerifier creates a new post verifier. +func NewPostVerifier(cfg PostConfig, logger *zap.Logger, opts ...PostVerifierOpt) (PostVerifier, error) { + options := &postVerifierOpts{ + opts: DefaultPostVerifyingOpts(), } + for _, o := range opts { + o(options) + } + if options.opts.Disabled { + logger.Warn("verifying post proofs is disabled") + return &noopPostVerifier{}, nil + } + + logger.Debug("creating post verifier") + verifier, err := verifying.NewProofVerifier(verifying.WithPowFlags(options.opts.Flags.Value())) + logger.Debug("created post verifier", zap.Error(err)) + if err != nil { + return nil, err + } + workers := options.opts.Workers + minWorkers := min(options.opts.MinWorkers, workers) + offloadingVerifier := newOffloadingPostVerifier( + &postVerifier{logger: logger, ProofVerifier: verifier, cfg: cfg.ToConfig()}, + workers, + logger, + options.prioritizedIds..., + ) + if options.autoscaling && minWorkers != workers { + offloadingVerifier.autoscale(minWorkers, workers) + } + return offloadingVerifier, nil } -// NewOffloadingPostVerifier creates a new post proof verifier with the given number of workers. +// newOffloadingPostVerifier creates a new post proof verifier with the given number of workers. // The verifier will distribute incoming proofs between the workers. // It will block if all workers are busy. // // SAFETY: The `verifier` must be safe to use concurrently. // // The verifier must be closed after use with Close(). -func NewOffloadingPostVerifier( +func newOffloadingPostVerifier( verifier PostVerifier, numWorkers int, logger *zap.Logger, - opts ...OffloadingPostVerifierOpt, -) *OffloadingPostVerifier { - v := &OffloadingPostVerifier{ + prioritizedIds ...types.NodeID, +) *offloadingPostVerifier { + v := &offloadingPostVerifier{ log: logger, workers: make([]*postVerifierWorker, 0, numWorkers), prioritized: make(chan *verifyPostJob, numWorkers), @@ -139,9 +178,8 @@ func NewOffloadingPostVerifier( verifier: verifier, prioritizedIds: make(map[types.NodeID]struct{}), } - - for _, o := range opts { - o(v) + for _, id := range prioritizedIds { + v.prioritizedIds[id] = struct{}{} } v.log.Info("starting post verifier") @@ -152,7 +190,7 @@ func NewOffloadingPostVerifier( // Turn on automatic scaling of the number of workers. // The number of workers will be scaled between `min` and `target` (inclusive). -func (v *OffloadingPostVerifier) Autoscale(min, target int) { +func (v *offloadingPostVerifier) autoscale(min, target int) { a, err := newAutoscaler() if err != nil { v.log.Panic("failed to create autoscaler", zap.Error(err)) @@ -165,7 +203,7 @@ func (v *OffloadingPostVerifier) Autoscale(min, target int) { // SAFETY: Must not be called concurrently. // This is satisfied by the fact that the only caller is the autoscaler, // which executes scale() serially. -func (v *OffloadingPostVerifier) scale(target int) { +func (v *offloadingPostVerifier) scale(target int) { v.log.Info("scaling post verifier", zap.Int("current", len(v.workers)), zap.Int("new", target)) if target > len(v.workers) { @@ -192,7 +230,7 @@ func (v *OffloadingPostVerifier) scale(target int) { } } -func (v *OffloadingPostVerifier) Verify( +func (v *offloadingPostVerifier) Verify( ctx context.Context, p *shared.Proof, m *shared.ProofMetadata, @@ -237,7 +275,7 @@ func (v *OffloadingPostVerifier) Verify( } } -func (v *OffloadingPostVerifier) Close() error { +func (v *offloadingPostVerifier) Close() error { select { case <-v.stop: return nil @@ -275,3 +313,18 @@ func (w *postVerifierWorker) start() { } } } + +type noopPostVerifier struct{} + +func (v *noopPostVerifier) Verify( + _ context.Context, + _ *shared.Proof, + _ *shared.ProofMetadata, + _ ...verifying.OptionFunc, +) error { + return nil +} + +func (v *noopPostVerifier) Close() error { + return nil +} diff --git a/activation/post_verifier_scaling_test.go b/activation/post_verifier_scaling_test.go index 8d0ff7c2d5..7fbb199319 100644 --- a/activation/post_verifier_scaling_test.go +++ b/activation/post_verifier_scaling_test.go @@ -48,7 +48,7 @@ func TestAutoScaling(t *testing.T) { func TestPostVerifierScaling(t *testing.T) { // 0 workers - no one will verify the proof mockVerifier := NewMockPostVerifier(gomock.NewController(t)) - v := NewOffloadingPostVerifier(mockVerifier, 0, zaptest.NewLogger(t)) + v := newOffloadingPostVerifier(mockVerifier, 0, zaptest.NewLogger(t)) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) defer cancel() diff --git a/activation/post_verifier_test.go b/activation/post_verifier_test.go index fb5a8140d2..db949914e7 100644 --- a/activation/post_verifier_test.go +++ b/activation/post_verifier_test.go @@ -1,4 +1,4 @@ -package activation_test +package activation import ( "context" @@ -9,10 +9,10 @@ import ( "github.com/spacemeshos/post/shared" "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/common/types" ) @@ -20,8 +20,8 @@ func TestOffloadingPostVerifier(t *testing.T) { proof := shared.Proof{} metadata := shared.ProofMetadata{} - verifier := activation.NewMockPostVerifier(gomock.NewController(t)) - offloadingVerifier := activation.NewOffloadingPostVerifier(verifier, 1, zaptest.NewLogger(t)) + verifier := NewMockPostVerifier(gomock.NewController(t)) + offloadingVerifier := newOffloadingPostVerifier(verifier, 1, zaptest.NewLogger(t)) defer offloadingVerifier.Close() verifier.EXPECT().Close().Return(nil) @@ -35,7 +35,7 @@ func TestOffloadingPostVerifier(t *testing.T) { } func TestPostVerifierDetectsInvalidProof(t *testing.T) { - verifier, err := activation.NewPostVerifier(activation.PostConfig{}, zaptest.NewLogger(t)) + verifier, err := NewPostVerifier(PostConfig{}, zaptest.NewLogger(t)) require.NoError(t, err) defer verifier.Close() require.Error(t, verifier.Verify(context.Background(), &shared.Proof{}, &shared.ProofMetadata{})) @@ -45,8 +45,8 @@ func TestPostVerifierVerifyAfterStop(t *testing.T) { proof := shared.Proof{} metadata := shared.ProofMetadata{} - verifier := activation.NewMockPostVerifier(gomock.NewController(t)) - offloadingVerifier := activation.NewOffloadingPostVerifier(verifier, 1, zaptest.NewLogger(t)) + verifier := NewMockPostVerifier(gomock.NewController(t)) + offloadingVerifier := newOffloadingPostVerifier(verifier, 1, zaptest.NewLogger(t)) defer offloadingVerifier.Close() verifier.EXPECT().Verify(gomock.Any(), &proof, &metadata, gomock.Any()).Return(nil) @@ -65,8 +65,8 @@ func TestPostVerifierNoRaceOnClose(t *testing.T) { var proof shared.Proof var metadata shared.ProofMetadata - verifier := activation.NewMockPostVerifier(gomock.NewController(t)) - offloadingVerifier := activation.NewOffloadingPostVerifier(verifier, 1, zaptest.NewLogger(t)) + verifier := NewMockPostVerifier(gomock.NewController(t)) + offloadingVerifier := newOffloadingPostVerifier(verifier, 1, zaptest.NewLogger(t)) defer offloadingVerifier.Close() verifier.EXPECT().Close().AnyTimes().Return(nil) @@ -91,9 +91,9 @@ func TestPostVerifierNoRaceOnClose(t *testing.T) { } func TestPostVerifierClose(t *testing.T) { - verifier := activation.NewMockPostVerifier(gomock.NewController(t)) + verifier := NewMockPostVerifier(gomock.NewController(t)) // 0 workers - no one will verify the proof - v := activation.NewOffloadingPostVerifier(verifier, 0, zaptest.NewLogger(t)) + v := newOffloadingPostVerifier(verifier, 0, zaptest.NewLogger(t)) verifier.EXPECT().Close().Return(nil) require.NoError(t, v.Close()) @@ -104,8 +104,8 @@ func TestPostVerifierClose(t *testing.T) { func TestPostVerifierPrioritization(t *testing.T) { nodeID := types.RandomNodeID() - verifier := activation.NewMockPostVerifier(gomock.NewController(t)) - v := activation.NewOffloadingPostVerifier(verifier, 2, zaptest.NewLogger(t), activation.PrioritizedIDs(nodeID)) + verifier := NewMockPostVerifier(gomock.NewController(t)) + v := newOffloadingPostVerifier(verifier, 2, zaptest.NewLogger(t), nodeID) verifier.EXPECT(). Verify(gomock.Any(), gomock.Any(), &shared.ProofMetadata{NodeId: nodeID.Bytes()}, gomock.Any()). @@ -114,3 +114,12 @@ func TestPostVerifierPrioritization(t *testing.T) { err := v.Verify(context.Background(), &shared.Proof{}, &shared.ProofMetadata{NodeId: nodeID.Bytes()}) require.NoError(t, err) } + +func TestVerificationDisabled(t *testing.T) { + opts := DefaultPostVerifyingOpts() + opts.Disabled = true + v, err := NewPostVerifier(DefaultPostConfig(), zap.NewNop(), WithVerifyingOpts(opts)) + require.NoError(t, err) + require.NoError(t, v.Verify(context.Background(), &shared.Proof{}, &shared.ProofMetadata{})) + require.NoError(t, v.Close()) +} diff --git a/checkpoint/recovery_test.go b/checkpoint/recovery_test.go index 129701fd5a..d6a066f423 100644 --- a/checkpoint/recovery_test.go +++ b/checkpoint/recovery_test.go @@ -252,7 +252,6 @@ func validateAndPreserveData( mreceiver, mtrtl, lg, - activation.PoetConfig{}, ) mfetch.EXPECT().GetAtxs(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() for i, vatx := range deps { diff --git a/cmd/root.go b/cmd/root.go index d8d56db1f7..768506f513 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -278,6 +278,13 @@ func AddCommands(cmd *cobra.Command) { /**======================== PoST Verifying Flags ========================== **/ + cmd.PersistentFlags().BoolVar( + &cfg.SMESHING.VerifyingOpts.Disabled, + "smeshing-opts-verifying-disable", + false, + "Disable verifying POST proofs. Experimental.\n"+ + "Use with caution, only on private nodes with a trusted public peer that validates the proofs.", + ) cmd.PersistentFlags().IntVar( &cfg.SMESHING.VerifyingOpts.MinWorkers, "smeshing-opts-verifying-min-workers", diff --git a/node/node.go b/node/node.go index db998b36ac..973e4e979b 100644 --- a/node/node.go +++ b/node/node.go @@ -24,7 +24,6 @@ import ( grpczap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" "github.com/mitchellh/mapstructure" "github.com/spacemeshos/poet/server" - "github.com/spacemeshos/post/verifying" "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -368,7 +367,7 @@ type App struct { tortoise *tortoise.Tortoise updater *bootstrap.Updater poetDb *activation.PoetDb - postVerifier *activation.OffloadingPostVerifier + postVerifier activation.PostVerifier postSupervisor *activation.PostSupervisor preserve *checkpoint.PreservedData errCh chan error @@ -564,27 +563,17 @@ func (app *App) initServices(ctx context.Context) error { poetDb := activation.NewPoetDb(app.db, app.addLogger(PoetDbLogger, lg)) - nipostValidatorLogger := app.addLogger(NipostValidatorLogger, lg) - - lg.Debug("creating post verifier") verifier, err := activation.NewPostVerifier( app.Config.POST, - nipostValidatorLogger.Zap(), - verifying.WithPowFlags(app.Config.SMESHING.VerifyingOpts.Flags.Value()), + app.addLogger(NipostValidatorLogger, lg).Zap(), + activation.WithVerifyingOpts(app.Config.SMESHING.VerifyingOpts), + activation.PrioritizedIDs(app.edSgn.NodeID()), + activation.WithAutoscaling(), ) - lg.With().Debug("created post verifier", log.Err(err)) if err != nil { - return err + return fmt.Errorf("creating post verifier: %w", err) } - minWorkers := app.Config.SMESHING.VerifyingOpts.MinWorkers - workers := app.Config.SMESHING.VerifyingOpts.Workers - app.postVerifier = activation.NewOffloadingPostVerifier( - verifier, - workers, - nipostValidatorLogger.Zap(), - activation.PrioritizedIDs(app.edSgn.NodeID()), - ) - app.postVerifier.Autoscale(minWorkers, workers) + app.postVerifier = verifier validator := activation.NewValidator( poetDb, @@ -715,7 +704,6 @@ func (app *App) initServices(ctx context.Context) error { beaconProtocol, trtl, app.addLogger(ATXHandlerLogger, lg), - app.Config.POET, ) atxHandler.Register(app.edSgn)