diff --git a/CHANGELOG.md b/CHANGELOG.md index a6309af2169..3a230704c71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,10 @@ See [RELEASE](./RELEASE.md) for workflow instructions. Bounds the time required to restart a node. +* [#5171](https://github.com/spacemeshos/go-spacemesh/pull/5171) Set minimal active set according to the observed number of atxs. + + It will prevent ballots that underreport observed atxs from spamming the network. It doesn't have impact on rewards. + * [#5169](https://github.com/spacemeshos/go-spacemesh/pull/5169) Support prunning activesets. As of epoch 6 activesets storage size is about ~1.5GB. They are not useful after verifying eligibilities diff --git a/blocks/certifier.go b/blocks/certifier.go index d20e2805528..7076cc1fb22 100644 --- a/blocks/certifier.go +++ b/blocks/certifier.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "sync" + "sync/atomic" "golang.org/x/sync/errgroup" @@ -50,13 +51,6 @@ func defaultCertConfig() CertConfig { // CertifierOpt for configuring Certifier. type CertifierOpt func(*Certifier) -// WithCertContext modifies parent context for Certifier. -func WithCertContext(ctx context.Context) CertifierOpt { - return func(c *Certifier) { - c.ctx = ctx - } -} - // WithCertConfig defines cfg for Certifier. func WithCertConfig(cfg CertConfig) CertifierOpt { return func(c *Certifier) { @@ -83,8 +77,9 @@ type Certifier struct { cfg CertConfig once sync.Once eg errgroup.Group - ctx context.Context - cancel func() + + stop func() + stopped atomic.Bool db *datastore.CachedDB oracle hare.Rolacle @@ -119,7 +114,6 @@ func NewCertifier( c := &Certifier{ logger: log.NewNop(), cfg: defaultCertConfig(), - ctx: context.Background(), db: db, oracle: o, nodeID: n, @@ -137,44 +131,39 @@ func NewCertifier( } c.collector = newCollector(c) - c.ctx, c.cancel = context.WithCancel(c.ctx) return c } // Start starts the background goroutine for periodic pruning. -func (c *Certifier) Start() { +func (c *Certifier) Start(ctx context.Context) { c.once.Do(func() { + ctx, c.stop = context.WithCancel(ctx) c.eg.Go(func() error { - return c.run() + return c.run(ctx) }) }) } // Stop stops the outstanding goroutines. func (c *Certifier) Stop() { - c.cancel() + c.stopped.Store(true) + if c.stop == nil { + return // not started + } + c.stop() err := c.eg.Wait() if err != nil && !errors.Is(err, context.Canceled) { - c.logger.With().Error("blockGen task failure", log.Err(err)) - } -} - -func (c *Certifier) isShuttingDown() bool { - select { - case <-c.ctx.Done(): - return true - default: - return false + c.logger.With().Error("certifier task failure", log.Err(err)) } } -func (c *Certifier) run() error { +func (c *Certifier) run(ctx context.Context) error { for layer := c.layerClock.CurrentLayer(); ; layer = layer.Add(1) { select { case <-c.layerClock.AwaitLayer(layer): c.prune() - case <-c.ctx.Done(): - return fmt.Errorf("context done: %w", c.ctx.Err()) + case <-ctx.Done(): + return fmt.Errorf("context done: %w", ctx.Err()) } } } @@ -335,7 +324,7 @@ func (c *Certifier) HandleCertifyMessage(ctx context.Context, peer p2p.Peer, dat // HandleCertifyMessage is the gossip receiver for certify message. func (c *Certifier) handleCertifyMessage(ctx context.Context, _ p2p.Peer, data []byte) error { - if c.isShuttingDown() { + if c.stopped.Load() { return errors.New("certifier shutting down") } diff --git a/blocks/certifier_test.go b/blocks/certifier_test.go index d41483472a0..a9ce829f932 100644 --- a/blocks/certifier_test.go +++ b/blocks/certifier_test.go @@ -143,9 +143,9 @@ func TestStartStop(t *testing.T) { func(_ types.LayerID) <-chan struct{} { return ch }).AnyTimes() - tc.Start() + tc.Start(context.Background()) ch <- struct{}{} - tc.Start() // calling Start() for the second time have no effect + tc.Start(context.Background()) // calling Start() for the second time have no effect tc.Stop() } @@ -591,7 +591,7 @@ func Test_OldLayersPruned(t *testing.T) { } return ch }).AnyTimes() - tc.Start() + tc.Start(context.Background()) ch <- struct{}{} // for current ch <- struct{}{} // for current+1 <-pruned diff --git a/blocks/generator.go b/blocks/generator.go index 3d5e389bd91..e4411a5be2f 100644 --- a/blocks/generator.go +++ b/blocks/generator.go @@ -28,8 +28,7 @@ type Generator struct { cfg Config once sync.Once eg errgroup.Group - ctx context.Context - cancel func() + stop func() cdb *datastore.CachedDB msh meshProvider @@ -60,13 +59,6 @@ func defaultConfig() Config { // GeneratorOpt for configuring Generator. type GeneratorOpt func(*Generator) -// WithContext modifies default context. -func WithContext(ctx context.Context) GeneratorOpt { - return func(g *Generator) { - g.ctx = ctx - } -} - // WithConfig defines cfg for Generator. func WithConfig(cfg Config) GeneratorOpt { return func(g *Generator) { @@ -101,7 +93,6 @@ func NewGenerator( g := &Generator{ logger: log.NewNop(), cfg: defaultConfig(), - ctx: context.Background(), cdb: cdb, msh: m, executor: exec, @@ -113,34 +104,35 @@ func NewGenerator( for _, opt := range opts { opt(g) } - g.ctx, g.cancel = context.WithCancel(g.ctx) + return g } // Start starts listening to hare output. -func (g *Generator) Start() { +func (g *Generator) Start(ctx context.Context) { g.once.Do(func() { + ctx, g.stop = context.WithCancel(ctx) g.eg.Go(func() error { - return g.run() + return g.run(ctx) }) }) } // Stop stops listening to hare output. func (g *Generator) Stop() { - g.cancel() + g.stop() err := g.eg.Wait() if err != nil && !errors.Is(err, context.Canceled) { g.logger.With().Error("blockGen task failure", log.Err(err)) } } -func (g *Generator) run() error { +func (g *Generator) run(ctx context.Context) error { var maxLayer types.LayerID for { select { - case <-g.ctx.Done(): - return fmt.Errorf("context done: %w", g.ctx.Err()) + case <-ctx.Done(): + return fmt.Errorf("context done: %w", ctx.Err()) case out := <-g.hareCh: g.logger.With().Debug("received hare output", log.Context(out.Ctx), diff --git a/blocks/generator_test.go b/blocks/generator_test.go index 678438589d8..5247443b09a 100644 --- a/blocks/generator_test.go +++ b/blocks/generator_test.go @@ -226,8 +226,8 @@ func checkRewards(t *testing.T, atxs []*types.ActivationTx, expWeightPer *big.Ra func Test_StartStop(t *testing.T) { tg := createTestGenerator(t) - tg.Start() - tg.Start() // start for the second time is ok. + tg.Start(context.Background()) + tg.Start(context.Background()) // start for the second time is ok. tg.Stop() } @@ -252,7 +252,7 @@ func genData(t *testing.T, cdb *datastore.CachedDB, lid types.LayerID, optimisti func Test_SerialExecution(t *testing.T) { tg := createTestGenerator(t) - tg.Start() + tg.Start(context.Background()) tg.mockFetch.EXPECT().GetProposals(gomock.Any(), gomock.Any()).AnyTimes() layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) @@ -399,7 +399,7 @@ func Test_run(t *testing.T) { return nil }) tg.mockPatrol.EXPECT().CompleteHare(layerID) - tg.Start() + tg.Start(context.Background()) tg.hareCh <- hare.LayerOutput{Ctx: context.Background(), Layer: layerID, Proposals: pids} require.Eventually(t, func() bool { return len(tg.hareCh) == 0 }, time.Second, 100*time.Millisecond) tg.Stop() @@ -411,7 +411,7 @@ func Test_processHareOutput_EmptyOutput(t *testing.T) { tg := createTestGenerator(t) layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) - tg.Start() + tg.Start(context.Background()) tg.mockCert.EXPECT().RegisterForCert(gomock.Any(), layerID, types.EmptyBlockID) tg.mockCert.EXPECT().CertifyIfEligible(gomock.Any(), gomock.Any(), layerID, types.EmptyBlockID) tg.mockMesh.EXPECT().ProcessLayerPerHareOutput(gomock.Any(), layerID, types.EmptyBlockID, false) @@ -425,7 +425,7 @@ func Test_run_FetchFailed(t *testing.T) { tg := createTestGenerator(t) layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) - tg.Start() + tg.Start(context.Background()) pids := []types.ProposalID{{1}, {2}, {3}} tg.mockFetch.EXPECT().GetProposals(gomock.Any(), pids).DoAndReturn( func(_ context.Context, _ []types.ProposalID) error { @@ -441,7 +441,7 @@ func Test_run_DiffHasFromConsensus(t *testing.T) { tg := createTestGenerator(t) layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) - tg.Start() + tg.Start(context.Background()) // create multiple proposals with overlapping TXs txIDs := createAndSaveTxs(t, 100, tg.cdb) @@ -463,7 +463,7 @@ func Test_run_ExecuteFailed(t *testing.T) { tg := createTestGenerator(t) layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) - tg.Start() + tg.Start(context.Background()) txIDs := createAndSaveTxs(t, 100, tg.cdb) signers, atxes := createATXs(t, tg.cdb, (layerID.GetEpoch() - 1).FirstLayer(), 10) activeSet := types.ToATXIDs(atxes) @@ -488,7 +488,7 @@ func Test_run_AddBlockFailed(t *testing.T) { tg := createTestGenerator(t) layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) - tg.Start() + tg.Start(context.Background()) txIDs := createAndSaveTxs(t, 100, tg.cdb) signers, atxes := createATXs(t, tg.cdb, (layerID.GetEpoch() - 1).FirstLayer(), 10) activeSet := types.ToATXIDs(atxes) @@ -511,7 +511,7 @@ func Test_run_RegisterCertFailureIgnored(t *testing.T) { tg := createTestGenerator(t) layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) - tg.Start() + tg.Start(context.Background()) txIDs := createAndSaveTxs(t, 100, tg.cdb) signers, atxes := createATXs(t, tg.cdb, (layerID.GetEpoch() - 1).FirstLayer(), 10) activeSet := types.ToATXIDs(atxes) @@ -537,7 +537,7 @@ func Test_run_CertifyFailureIgnored(t *testing.T) { tg := createTestGenerator(t) layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) - tg.Start() + tg.Start(context.Background()) txIDs := createAndSaveTxs(t, 100, tg.cdb) signers, atxes := createATXs(t, tg.cdb, (layerID.GetEpoch() - 1).FirstLayer(), 10) activeSet := types.ToATXIDs(atxes) @@ -563,7 +563,7 @@ func Test_run_ProcessLayerFailed(t *testing.T) { tg := createTestGenerator(t) layerID := types.GetEffectiveGenesis().Add(100) require.NoError(t, layers.SetApplied(tg.cdb, layerID-1, types.EmptyBlockID)) - tg.Start() + tg.Start(context.Background()) txIDs := createAndSaveTxs(t, 100, tg.cdb) signers, atxes := createATXs(t, tg.cdb, (layerID.GetEpoch() - 1).FirstLayer(), 10) activeSet := types.ToATXIDs(atxes) diff --git a/cmd/activeset/activeset.go b/cmd/activeset/activeset.go new file mode 100644 index 00000000000..c941ac10509 --- /dev/null +++ b/cmd/activeset/activeset.go @@ -0,0 +1,53 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "os" + "strconv" + + "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/sql" + "github.com/spacemeshos/go-spacemesh/sql/atxs" +) + +func main() { + flag.Usage = func() { + fmt.Println(`Usage: + > activeset +Example: + query atxs that are published in epoch 3 and stored in state.sql file. + > activeset 3 state.sql`) + flag.PrintDefaults() + } + flag.Parse() + + publish, err := strconv.Atoi(flag.Arg(0)) + must(err, "publish epoch %v is not a valid integer: %s", flag.Arg(0), err) + dbpath := flag.Arg(1) + if len(dbpath) == 0 { + must(errors.New("dbpath is empty"), "dbpath is empty\n") + } + db, err := sql.Open("file:" + dbpath) + must(err, "can't open db at dbpath=%v. err=%s\n", dbpath, err) + + ids, err := atxs.GetIDsByEpoch(db, types.EpochID(publish)) + must(err, "get ids by epoch %d. dbpath=%v. err=%s\n", publish, dbpath, err) + var weight uint64 + for _, id := range ids { + atx, err := atxs.Get(db, id) + must(err, "get id %v: %s\n", id, err) + weight += atx.GetWeight() + } + fmt.Printf("count = %d\nweight = %d\n", len(ids), weight) +} + +func must(err error, msg string, vars ...any) { + if err != nil { + fmt.Printf(msg, vars...) + fmt.Println("") + flag.Usage() + os.Exit(1) + } +} diff --git a/common/types/min_weight.go b/common/types/min_weight.go new file mode 100644 index 00000000000..5b4692142fa --- /dev/null +++ b/common/types/min_weight.go @@ -0,0 +1,6 @@ +package types + +type EpochMinimalActiveWeight struct { + Epoch EpochID + Weight uint64 +} diff --git a/config/mainnet.go b/config/mainnet.go index e2f8f074c8d..ed68ecd370d 100644 --- a/config/mainnet.go +++ b/config/mainnet.go @@ -15,6 +15,7 @@ import ( "github.com/spacemeshos/go-spacemesh/beacon" "github.com/spacemeshos/go-spacemesh/bootstrap" "github.com/spacemeshos/go-spacemesh/checkpoint" + "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/fetch" hareConfig "github.com/spacemeshos/go-spacemesh/hare/config" @@ -96,9 +97,12 @@ func MainnetConfig() Config { WindowSize: 10000, MaxExceptions: 1000, BadBeaconVoteDelayLayers: 4032, - // 1000 - is assumed minimal number of units - // 5000 - half of the expected poet ticks - MinimalActiveSetWeight: 1000 * 5000, + MinimalActiveSetWeight: []types.EpochMinimalActiveWeight{ + {Weight: 1_000_000}, + // generated using ./cmd/activeset for publish epoch 6 + // it will be used starting from epoch 8, because we will only release it in 7th + {Epoch: 8, Weight: 7_879_129_244}, + }, }, HARE: hareConfig.Config{ N: 200, diff --git a/config/presets/testnet.go b/config/presets/testnet.go index e0678111b4e..2d1bfb99acd 100644 --- a/config/presets/testnet.go +++ b/config/presets/testnet.go @@ -15,6 +15,7 @@ import ( "github.com/spacemeshos/go-spacemesh/beacon" "github.com/spacemeshos/go-spacemesh/bootstrap" "github.com/spacemeshos/go-spacemesh/checkpoint" + "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/config" "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/fetch" @@ -78,9 +79,7 @@ func testnet() config.Config { WindowSize: 10000, MaxExceptions: 1000, BadBeaconVoteDelayLayers: 4032, - // 100 - is assumed minimal number of units - // 100 - half of the expected poet ticks - MinimalActiveSetWeight: 100 * 100, + MinimalActiveSetWeight: []types.EpochMinimalActiveWeight{{Weight: 10_000}}, }, HARE: hareConfig.Config{ N: 200, diff --git a/miner/minweight/minweight.go b/miner/minweight/minweight.go new file mode 100644 index 00000000000..e5859e5e721 --- /dev/null +++ b/miner/minweight/minweight.go @@ -0,0 +1,20 @@ +package minweight + +import "github.com/spacemeshos/go-spacemesh/common/types" + +func Select(epoch types.EpochID, weights []types.EpochMinimalActiveWeight) uint64 { + var ( + rst uint64 + prev types.EpochID + ) + for _, weight := range weights { + if weight.Epoch < prev { + panic("weights are not sorted by epoch") + } + if epoch >= weight.Epoch { + rst = weight.Weight + } + prev = weight.Epoch + } + return rst +} diff --git a/miner/minweight/minweight_test.go b/miner/minweight/minweight_test.go new file mode 100644 index 00000000000..7aa959d9044 --- /dev/null +++ b/miner/minweight/minweight_test.go @@ -0,0 +1,49 @@ +package minweight + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/spacemeshos/go-spacemesh/common/types" +) + +func TestSelect(t *testing.T) { + t.Run("empty", func(t *testing.T) { + require.EqualValues(t, 0, Select(0, nil)) + }) + t.Run("sorted", func(t *testing.T) { + require.EqualValues(t, 10, Select(5, []types.EpochMinimalActiveWeight{ + {Epoch: 0, Weight: 1}, + {Epoch: 4, Weight: 5}, + {Epoch: 5, Weight: 10}, + {Epoch: 7, Weight: 11}, + })) + }) + t.Run("in-between epochs", func(t *testing.T) { + require.EqualValues(t, 10, Select(6, []types.EpochMinimalActiveWeight{ + {Epoch: 0, Weight: 1}, + {Epoch: 4, Weight: 5}, + {Epoch: 5, Weight: 10}, + {Epoch: 7, Weight: 11}, + })) + }) + t.Run("after all", func(t *testing.T) { + require.EqualValues(t, 11, Select(10, []types.EpochMinimalActiveWeight{ + {Epoch: 0, Weight: 1}, + {Epoch: 4, Weight: 5}, + {Epoch: 5, Weight: 10}, + {Epoch: 7, Weight: 11}, + })) + }) + t.Run("not sorted panic", func(t *testing.T) { + require.Panics(t, func() { + Select(5, []types.EpochMinimalActiveWeight{ + {Epoch: 0, Weight: 1}, + {Epoch: 5, Weight: 10}, + {Epoch: 4, Weight: 5}, + {Epoch: 7, Weight: 11}, + }) + }) + }) +} diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index 0179f2dc517..e14f2e923d5 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -20,6 +20,7 @@ import ( "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/events" "github.com/spacemeshos/go-spacemesh/log" + "github.com/spacemeshos/go-spacemesh/miner/minweight" "github.com/spacemeshos/go-spacemesh/p2p/pubsub" "github.com/spacemeshos/go-spacemesh/proposals" "github.com/spacemeshos/go-spacemesh/signing" @@ -147,9 +148,9 @@ type config struct { layerSize uint32 layersPerEpoch uint32 hdist uint32 - minActiveSetWeight uint64 networkDelay time.Duration workersLimit int + minActiveSetWeight []types.EpochMinimalActiveWeight // used to determine whether a node has enough information on the active set this epoch goodAtxPercent int } @@ -158,7 +159,6 @@ func (c *config) MarshalLogObject(encoder log.ObjectEncoder) error { encoder.AddUint32("layer size", c.layerSize) encoder.AddUint32("epoch size", c.layersPerEpoch) encoder.AddUint32("hdist", c.hdist) - encoder.AddUint64("min active weight", c.minActiveSetWeight) encoder.AddDuration("network delay", c.networkDelay) encoder.AddInt("good atx percent", c.goodAtxPercent) return nil @@ -189,7 +189,7 @@ func WithLayerPerEpoch(layers uint32) Opt { } } -func WithMinimalActiveSetWeight(weight uint64) Opt { +func WithMinimalActiveSetWeight(weight []types.EpochMinimalActiveWeight) Opt { return func(pb *ProposalBuilder) { pb.cfg.minActiveSetWeight = weight } @@ -439,7 +439,7 @@ func (pb *ProposalBuilder) initSignerData( ss.session.beacon = pb.shared.beacon ss.session.eligibilities.slots = proposals.MustGetNumEligibleSlots( ss.session.atxWeight, - pb.cfg.minActiveSetWeight, + minweight.Select(lid.GetEpoch(), pb.cfg.minActiveSetWeight), pb.shared.active.weight, pb.cfg.layerSize, pb.cfg.layersPerEpoch, diff --git a/miner/proposal_builder_test.go b/miner/proposal_builder_test.go index 9b9b4e829e8..0047f1f0067 100644 --- a/miner/proposal_builder_test.go +++ b/miner/proposal_builder_test.go @@ -273,7 +273,7 @@ func TestBuild(t *testing.T) { }, { desc: "min active weight", - opts: []Opt{WithMinimalActiveSetWeight(1000)}, + opts: []Opt{WithMinimalActiveSetWeight([]types.EpochMinimalActiveWeight{{Weight: 1000}})}, steps: []step{ { lid: 15, diff --git a/node/node.go b/node/node.go index 2c173736d56..58d2aa7cea7 100644 --- a/node/node.go +++ b/node/node.go @@ -486,8 +486,8 @@ func (app *App) setupLogging() { } func (app *App) getAppInfo() string { - return fmt.Sprintf("App version: %s. Git: %s - %s . Go Version: %s. OS: %s-%s ", - cmd.Version, cmd.Branch, cmd.Commit, runtime.Version(), runtime.GOOS, runtime.GOARCH) + return fmt.Sprintf("App version: %s. Git: %s - %s . Go Version: %s. OS: %s-%s . Genesis %s", + cmd.Version, cmd.Branch, cmd.Commit, runtime.Version(), runtime.GOOS, runtime.GOARCH, app.Config.Genesis.GenesisID().String()) } // Cleanup stops all app services. @@ -786,7 +786,6 @@ func (app *App) initServices(ctx context.Context) error { app.clock, beaconProtocol, trtl, - blocks.WithCertContext(ctx), blocks.WithCertConfig(blocks.CertConfig{ CommitteeSize: app.Config.HARE.N, CertifyThreshold: app.Config.HARE.N/2 + 1, @@ -836,7 +835,6 @@ func (app *App) initServices(ctx context.Context) error { fetcherWrapped, app.certifier, patrol, - blocks.WithContext(ctx), blocks.WithConfig(blocks.Config{ BlockGasLimit: app.Config.BlockGasLimit, OptFilterThreshold: app.Config.OptFilterThreshold, @@ -1214,8 +1212,8 @@ func (app *App) startServices(ctx context.Context) error { app.syncer.Start() app.beaconProtocol.Start(ctx) - app.blockGen.Start() - app.certifier.Start() + app.blockGen.Start(ctx) + app.certifier.Start(ctx) if err := app.hare.Start(ctx); err != nil { return fmt.Errorf("cannot start hare: %w", err) } diff --git a/proposals/eligibility_validator.go b/proposals/eligibility_validator.go index ca95088ad00..a0c56a86766 100644 --- a/proposals/eligibility_validator.go +++ b/proposals/eligibility_validator.go @@ -10,6 +10,7 @@ import ( "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/log" + "github.com/spacemeshos/go-spacemesh/miner/minweight" "github.com/spacemeshos/go-spacemesh/system" ) @@ -26,7 +27,7 @@ var ( // Validator validates the eligibility of a Ballot. // the validation focuses on eligibility only and assumes the Ballot to be valid otherwise. type Validator struct { - minActiveSetWeight uint64 + minActiveSetWeight []types.EpochMinimalActiveWeight avgLayerSize uint32 layersPerEpoch uint32 tortoise tortoiseProvider @@ -49,7 +50,15 @@ func WithNonceFetcher(nf nonceFetcher) ValidatorOpt { // NewEligibilityValidator returns a new EligibilityValidator. func NewEligibilityValidator( - avgLayerSize, layersPerEpoch uint32, minActiveSetWeight uint64, clock layerClock, tortoise tortoiseProvider, cdb *datastore.CachedDB, bc system.BeaconCollector, lg log.Log, vrfVerifier vrfVerifier, opts ...ValidatorOpt, + avgLayerSize, layersPerEpoch uint32, + minActiveSetWeight []types.EpochMinimalActiveWeight, + clock layerClock, + tortoise tortoiseProvider, + cdb *datastore.CachedDB, + bc system.BeaconCollector, + lg log.Log, + vrfVerifier vrfVerifier, + opts ...ValidatorOpt, ) *Validator { v := &Validator{ minActiveSetWeight: minActiveSetWeight, @@ -151,7 +160,13 @@ func (v *Validator) validateReference(ballot *types.Ballot, actives []types.ATXI } totalWeight += atx.GetWeight() } - numEligibleSlots, err := GetNumEligibleSlots(owned.GetWeight(), v.minActiveSetWeight, totalWeight, v.avgLayerSize, v.layersPerEpoch) + numEligibleSlots, err := GetNumEligibleSlots( + owned.GetWeight(), + minweight.Select(ballot.Layer.GetEpoch(), v.minActiveSetWeight), + totalWeight, + v.avgLayerSize, + v.layersPerEpoch, + ) if err != nil { return nil, err } diff --git a/proposals/eligibility_validator_test.go b/proposals/eligibility_validator_test.go index ce4a866e6af..f90dfcad679 100644 --- a/proposals/eligibility_validator_test.go +++ b/proposals/eligibility_validator_test.go @@ -561,7 +561,7 @@ func TestEligibilityValidator(t *testing.T) { lg := logtest.New(t) db := datastore.NewCachedDB(sql.InMemory(), lg) - tv := NewEligibilityValidator(layerAvgSize, layersPerEpoch, tc.minWeight, ms.mclock, ms.md, + tv := NewEligibilityValidator(layerAvgSize, layersPerEpoch, []types.EpochMinimalActiveWeight{{Weight: tc.minWeight}}, ms.mclock, ms.md, db, ms.mbc, lg, ms.mvrf, WithNonceFetcher(db), ) diff --git a/proposals/handler.go b/proposals/handler.go index 93352312cbd..0e11151ca48 100644 --- a/proposals/handler.go +++ b/proposals/handler.go @@ -65,7 +65,7 @@ type Config struct { GoldenATXID types.ATXID MaxExceptions int Hdist uint32 - MinimalActiveSetWeight uint64 + MinimalActiveSetWeight []types.EpochMinimalActiveWeight } // defaultConfig for BlockHandler. diff --git a/tortoise/algorithm.go b/tortoise/algorithm.go index c45c44ac43b..61f792e498d 100644 --- a/tortoise/algorithm.go +++ b/tortoise/algorithm.go @@ -24,10 +24,10 @@ type Config struct { BadBeaconVoteDelayLayers uint32 `mapstructure:"tortoise-delay-layers"` // EnableTracer will write tortoise traces to the stderr. EnableTracer bool `mapstructure:"tortoise-enable-tracer"` - // MinimalActiveSetWeight denotes weight that will replace weight + // MinimalActiveSetWeight is a weight that will replace weight // recorded in the first ballot, if that weight is less than minimal // for purposes of eligibility computation. - MinimalActiveSetWeight uint64 `mapstructure:"tortoise-activeset-weight"` + MinimalActiveSetWeight []types.EpochMinimalActiveWeight LayerSize uint32 } diff --git a/tortoise/fixture_test.go b/tortoise/fixture_test.go index 5593ef75f31..7ece25e602c 100644 --- a/tortoise/fixture_test.go +++ b/tortoise/fixture_test.go @@ -721,12 +721,6 @@ func (s *session) withDelay(val uint32) *session { return s } -func (s *session) withMinActiveSetWeight(weight uint64) *session { - s.ensureConfig() - s.config.MinimalActiveSetWeight = weight - return s -} - func (s *session) tortoise() *Tortoise { s.ensureConfig() trt, err := New(WithLogger(logtest.New(s.tb)), WithConfig(*s.config)) diff --git a/tortoise/tortoise_test.go b/tortoise/tortoise_test.go index bc2b86629b0..6b9bfb06b7f 100644 --- a/tortoise/tortoise_test.go +++ b/tortoise/tortoise_test.go @@ -3207,22 +3207,6 @@ func TestUpdates(t *testing.T) { }) } -func TestMinimalActiveSetWeight(t *testing.T) { - s := newSession(t). - withMinActiveSetWeight(1000) - - s.smesher(0).atx(1, new(aopt).height(10).weight(2)) - s.beacon(1, "a") - s.smesher(0).atx(1).ballot(1, new(bopt). - totalEligibilities(s.epochEligibilities()). - beacon("a"). - eligibilities(1), - ) - s.tallyWait(1) - s.updates(t, new(results).verified(0).next(1)) - s.runInorder() -} - func TestDuplicateBallot(t *testing.T) { s := newSession(t) s.smesher(0).atx(1, new(aopt).height(10).weight(2))