From aa68ff96f13457f5d20cfe8223eed4da566f11b7 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) ## Motivation 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 | 5 ++ 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, 125 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35dbf6c6ca..e6a5a8ae0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,11 @@ configuration is as follows: ### 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 * [#5518](https://github.com/spacemeshos/go-spacemesh/pull/5518) In rare cases the node could create a malfeasance 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 78dda171ea..a9d73d09dd 100644 --- a/activation/handler_test.go +++ b/activation/handler_test.go @@ -123,7 +123,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 6152d1c83c..8351eb6114 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 877212aaa4..58f8afd701 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 1fec6e1daa..4682e79fc1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -285,6 +285,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 b09d2a5faa..153e406eea 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" @@ -384,7 +383,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 @@ -579,27 +578,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) } - workers := app.Config.SMESHING.VerifyingOpts.Workers - minWorkers := min(app.Config.SMESHING.VerifyingOpts.MinWorkers, 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, @@ -730,7 +719,6 @@ func (app *App) initServices(ctx context.Context) error { beaconProtocol, trtl, app.addLogger(ATXHandlerLogger, lg), - app.Config.POET, ) atxHandler.Register(app.edSgn)