Skip to content

Commit

Permalink
setup min activeset weight with data from latest epoch (#5171)
Browse files Browse the repository at this point in the history
closes: #5137

- added a tool that can be used to read db and compute total weight of atxs in the latest epoch
- activeset weight configured as  a sorted array of tuples (epoch, weight). components select latest tuple that match epoch based on timing of usage
  • Loading branch information
dshulyak committed Oct 20, 2023
1 parent d13d18f commit f1a9fe1
Show file tree
Hide file tree
Showing 16 changed files with 170 additions and 42 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 53 additions & 0 deletions cmd/activeset/activeset.go
Original file line number Diff line number Diff line change
@@ -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 <publish epoch> <db path>
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)
}
}
6 changes: 6 additions & 0 deletions common/types/min_weight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package types

type EpochMinimalActiveWeight struct {
Epoch EpochID
Weight uint64
}
10 changes: 7 additions & 3 deletions config/mainnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 2 additions & 3 deletions config/presets/testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down
20 changes: 20 additions & 0 deletions miner/minweight/minweight.go
Original file line number Diff line number Diff line change
@@ -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
}
49 changes: 49 additions & 0 deletions miner/minweight/minweight_test.go
Original file line number Diff line number Diff line change
@@ -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},
})
})
})
}
8 changes: 4 additions & 4 deletions miner/proposal_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion miner/proposal_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 18 additions & 3 deletions proposals/eligibility_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion proposals/eligibility_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
)
Expand Down
2 changes: 1 addition & 1 deletion proposals/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ type Config struct {
GoldenATXID types.ATXID
MaxExceptions int
Hdist uint32
MinimalActiveSetWeight uint64
MinimalActiveSetWeight []types.EpochMinimalActiveWeight
}

// defaultConfig for BlockHandler.
Expand Down
4 changes: 2 additions & 2 deletions tortoise/algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
6 changes: 0 additions & 6 deletions tortoise/fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
16 changes: 0 additions & 16 deletions tortoise/tortoise_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down

0 comments on commit f1a9fe1

Please sign in to comment.