Skip to content

Commit

Permalink
Merge pull request #5526 from spacemeshos/backport-1.3-5517
Browse files Browse the repository at this point in the history
Backport #5517
  • Loading branch information
poszu committed Jan 31, 2024
2 parents 2280341 + c307288 commit b03e89b
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 63 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 0 additions & 3 deletions activation/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -75,7 +74,6 @@ func NewHandler(
beacon AtxReceiver,
tortoise system.Tortoise,
log log.Log,
poetCfg PoetConfig,
) *Handler {
return &Handler{
local: local,
Expand All @@ -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),
Expand Down
1 change: 0 additions & 1 deletion activation/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ func newTestHandler(tb testing.TB, goldenATXID types.ATXID) *testHandler {
mbeacon,
mtortoise,
lg,
PoetConfig{},
)
return &testHandler{
Handler: atxHdlr,
Expand Down
5 changes: 5 additions & 0 deletions activation/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
103 changes: 78 additions & 25 deletions activation/post_verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand All @@ -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")
Expand All @@ -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))
Expand All @@ -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) {
Expand All @@ -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,
Expand Down Expand Up @@ -237,7 +275,7 @@ func (v *OffloadingPostVerifier) Verify(
}
}

func (v *OffloadingPostVerifier) Close() error {
func (v *offloadingPostVerifier) Close() error {
select {
case <-v.stop:
return nil
Expand Down Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion activation/post_verifier_scaling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
35 changes: 22 additions & 13 deletions activation/post_verifier_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package activation_test
package activation

import (
"context"
Expand All @@ -9,19 +9,19 @@ 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"
)

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)

Expand All @@ -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{}))
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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())
Expand All @@ -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()).
Expand All @@ -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())
}
1 change: 0 additions & 1 deletion checkpoint/recovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit b03e89b

Please sign in to comment.