Commit 95e31d3c authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6001 from ethereum-optimism/aj/peer-scores-single-option

op-node: Use single option for peer scoring level
parents d725759c b03d22e7
...@@ -52,7 +52,7 @@ func Main(cliCtx *cli.Context) error { ...@@ -52,7 +52,7 @@ func Main(cliCtx *cli.Context) error {
return err return err
} }
p2pConfig, err := p2pcli.NewConfig(cliCtx, config.BlockTime) p2pConfig, err := p2pcli.NewConfig(cliCtx, config)
if err != nil { if err != nil {
return fmt.Errorf("failed to load p2p config: %w", err) return fmt.Errorf("failed to load p2p config: %w", err)
} }
......
...@@ -813,10 +813,10 @@ func TestSystemDenseTopology(t *testing.T) { ...@@ -813,10 +813,10 @@ func TestSystemDenseTopology(t *testing.T) {
// Set peer scoring for each node, but without banning // Set peer scoring for each node, but without banning
for _, node := range cfg.Nodes { for _, node := range cfg.Nodes {
params, err := p2p.GetPeerScoreParams("light", 2) params, err := p2p.GetScoringParams("light", &node.Rollup)
require.NoError(t, err) require.NoError(t, err)
node.P2P = &p2p.Config{ node.P2P = &p2p.Config{
PeerScoring: &params, ScoringParams: params,
BanningEnabled: false, BanningEnabled: false,
} }
} }
......
package flags package flags
import ( import (
"fmt"
"time" "time"
"github.com/urfave/cli" "github.com/urfave/cli"
...@@ -25,15 +26,18 @@ var ( ...@@ -25,15 +26,18 @@ var (
Required: false, Required: false,
EnvVar: p2pEnv("NO_DISCOVERY"), EnvVar: p2pEnv("NO_DISCOVERY"),
} }
PeerScoring = cli.StringFlag{ Scoring = cli.StringFlag{
Name: "p2p.scoring.peers", Name: "p2p.scoring",
Usage: "Sets the peer scoring strategy for the P2P stack. " + Usage: "Sets the peer scoring strategy for the P2P stack. Can be one of: none or light.",
"Can be one of: none or light." +
"Custom scoring strategies can be defined in the config file.",
Required: false, Required: false,
Value: "none",
EnvVar: p2pEnv("PEER_SCORING"), EnvVar: p2pEnv("PEER_SCORING"),
} }
PeerScoring = cli.StringFlag{
Name: "p2p.scoring.peers",
Usage: fmt.Sprintf("Deprecated: Use %v instead", Scoring.Name),
Required: false,
Hidden: true,
}
PeerScoreBands = cli.StringFlag{ PeerScoreBands = cli.StringFlag{
Name: "p2p.score.bands", Name: "p2p.score.bands",
Usage: "Deprecated. This option is ignored and is only present for backwards compatibility.", Usage: "Deprecated. This option is ignored and is only present for backwards compatibility.",
...@@ -65,13 +69,10 @@ var ( ...@@ -65,13 +69,10 @@ var (
} }
TopicScoring = cli.StringFlag{ TopicScoring = cli.StringFlag{
Name: "p2p.scoring.topics", Name: "p2p.scoring.topics",
Usage: "Sets the topic scoring strategy. " + Usage: fmt.Sprintf("Deprecated: Use %v instead", Scoring.Name),
"Can be one of: none or light." +
"Custom scoring strategies can be defined in the config file.",
Required: false, Required: false,
Value: "none", Hidden: true,
EnvVar: p2pEnv("TOPIC_SCORING"),
} }
P2PPrivPath = cli.StringFlag{ P2PPrivPath = cli.StringFlag{
Name: "p2p.priv.path", Name: "p2p.priv.path",
...@@ -303,6 +304,7 @@ var p2pFlags = []cli.Flag{ ...@@ -303,6 +304,7 @@ var p2pFlags = []cli.Flag{
NoDiscovery, NoDiscovery,
P2PPrivPath, P2PPrivPath,
P2PPrivRaw, P2PPrivRaw,
Scoring,
PeerScoring, PeerScoring,
PeerScoreBands, PeerScoreBands,
Banning, Banning,
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-node/rollup"
ds "github.com/ipfs/go-datastore" ds "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-datastore/sync"
leveldb "github.com/ipfs/go-ds-leveldb" leveldb "github.com/ipfs/go-ds-leveldb"
...@@ -24,7 +25,7 @@ import ( ...@@ -24,7 +25,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
) )
func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) { func NewConfig(ctx *cli.Context, rollupCfg *rollup.Config) (*p2p.Config, error) {
conf := &p2p.Config{} conf := &p2p.Config{}
if ctx.GlobalBool(flags.DisableP2P.Name) { if ctx.GlobalBool(flags.DisableP2P.Name) {
...@@ -54,7 +55,7 @@ func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) { ...@@ -54,7 +55,7 @@ func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) {
return nil, fmt.Errorf("failed to load p2p gossip options: %w", err) return nil, fmt.Errorf("failed to load p2p gossip options: %w", err)
} }
if err := loadPeerScoringParams(conf, ctx, blockTime); err != nil { if err := loadScoringParams(conf, ctx, rollupCfg); err != nil {
return nil, fmt.Errorf("failed to load p2p peer scoring options: %w", err) return nil, fmt.Errorf("failed to load p2p peer scoring options: %w", err)
} }
...@@ -62,10 +63,6 @@ func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) { ...@@ -62,10 +63,6 @@ func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) {
return nil, fmt.Errorf("failed to load banning option: %w", err) return nil, fmt.Errorf("failed to load banning option: %w", err)
} }
if err := loadTopicScoringParams(conf, ctx, blockTime); err != nil {
return nil, fmt.Errorf("failed to load p2p topic scoring options: %w", err)
}
conf.EnableReqRespSync = ctx.GlobalBool(flags.SyncReqRespFlag.Name) conf.EnableReqRespSync = ctx.GlobalBool(flags.SyncReqRespFlag.Name)
return conf, nil return conf, nil
...@@ -84,37 +81,22 @@ func validatePort(p uint) (uint16, error) { ...@@ -84,37 +81,22 @@ func validatePort(p uint) (uint16, error) {
return uint16(p), nil return uint16(p), nil
} }
// loadTopicScoringParams loads the topic scoring options from the CLI context. // loadScoringParams loads the peer scoring options from the CLI context.
// func loadScoringParams(conf *p2p.Config, ctx *cli.Context, rollupCfg *rollup.Config) error {
// If the topic scoring options are not set, then the default topic scoring. scoringLevel := ctx.GlobalString(flags.Scoring.Name)
func loadTopicScoringParams(conf *p2p.Config, ctx *cli.Context, blockTime uint64) error { // Check old names for backwards compatibility
scoringLevel := ctx.GlobalString(flags.TopicScoring.Name) if scoringLevel == "" {
if scoringLevel != "" { scoringLevel = ctx.GlobalString(flags.PeerScoring.Name)
// Set default block topic scoring parameters }
// See prysm: https://github.com/prysmaticlabs/prysm/blob/develop/beacon-chain/p2p/gossip_scoring_params.go if scoringLevel == "" {
// And research from lighthouse: https://gist.github.com/blacktemplar/5c1862cb3f0e32a1a7fb0b25e79e6e2c scoringLevel = ctx.GlobalString(flags.TopicScoring.Name)
// And docs: https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#topic-parameter-calculation-and-decay
topicScoreParams, err := p2p.GetTopicScoreParams(scoringLevel, blockTime)
if err != nil {
return err
}
conf.TopicScoring = &topicScoreParams
} }
return nil
}
// loadPeerScoringParams loads the scoring options from the CLI context.
//
// If the scoring level is not set, no scoring is enabled.
func loadPeerScoringParams(conf *p2p.Config, ctx *cli.Context, blockTime uint64) error {
scoringLevel := ctx.GlobalString(flags.PeerScoring.Name)
if scoringLevel != "" { if scoringLevel != "" {
peerScoreParams, err := p2p.GetPeerScoreParams(scoringLevel, blockTime) params, err := p2p.GetScoringParams(scoringLevel, rollupCfg)
if err != nil { if err != nil {
return err return err
} }
conf.PeerScoring = &peerScoreParams conf.ScoringParams = params
} }
return nil return nil
......
...@@ -51,6 +51,11 @@ type SetupP2P interface { ...@@ -51,6 +51,11 @@ type SetupP2P interface {
ReqRespSyncEnabled() bool ReqRespSyncEnabled() bool
} }
// ScoringParams defines the various types of peer scoring parameters.
type ScoringParams struct {
PeerScoring pubsub.PeerScoreParams
}
// Config sets up a p2p host and discv5 service from configuration. // Config sets up a p2p host and discv5 service from configuration.
// This implements SetupP2P. // This implements SetupP2P.
type Config struct { type Config struct {
...@@ -62,9 +67,7 @@ type Config struct { ...@@ -62,9 +67,7 @@ type Config struct {
// Enable P2P-based alt-syncing method (req-resp protocol, not gossip) // Enable P2P-based alt-syncing method (req-resp protocol, not gossip)
AltSync bool AltSync bool
// Pubsub Scoring Parameters ScoringParams *ScoringParams
PeerScoring *pubsub.PeerScoreParams
TopicScoring *pubsub.TopicScoreParams
// Whether to ban peers based on their [PeerScoring] score. Should be negative. // Whether to ban peers based on their [PeerScoring] score. Should be negative.
BanningEnabled bool BanningEnabled bool
...@@ -135,7 +138,10 @@ func (conf *Config) Disabled() bool { ...@@ -135,7 +138,10 @@ func (conf *Config) Disabled() bool {
} }
func (conf *Config) PeerScoringParams() *pubsub.PeerScoreParams { func (conf *Config) PeerScoringParams() *pubsub.PeerScoreParams {
return conf.PeerScoring if conf.ScoringParams == nil {
return nil
}
return &conf.ScoringParams.PeerScoring
} }
func (conf *Config) BanPeers() bool { func (conf *Config) BanPeers() bool {
...@@ -150,10 +156,6 @@ func (conf *Config) BanDuration() time.Duration { ...@@ -150,10 +156,6 @@ func (conf *Config) BanDuration() time.Duration {
return conf.BanningDuration return conf.BanningDuration
} }
func (conf *Config) TopicScoringParams() *pubsub.TopicScoreParams {
return conf.TopicScoring
}
func (conf *Config) ReqRespSyncEnabled() bool { func (conf *Config) ReqRespSyncEnabled() bool {
return conf.EnableReqRespSync return conf.EnableReqRespSync
} }
......
...@@ -52,7 +52,6 @@ var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0} ...@@ -52,7 +52,6 @@ var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0}
type GossipSetupConfigurables interface { type GossipSetupConfigurables interface {
PeerScoringParams() *pubsub.PeerScoreParams PeerScoringParams() *pubsub.PeerScoreParams
TopicScoringParams() *pubsub.TopicScoreParams
// ConfigureGossip creates configuration options to apply to the GossipSub setup // ConfigureGossip creates configuration options to apply to the GossipSub setup
ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option
} }
...@@ -423,7 +422,7 @@ func (p *publisher) Close() error { ...@@ -423,7 +422,7 @@ func (p *publisher) Close() error {
return p.blocksTopic.Close() return p.blocksTopic.Close()
} }
func JoinGossip(p2pCtx context.Context, self peer.ID, topicScoreParams *pubsub.TopicScoreParams, ps *pubsub.PubSub, log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, gossipIn GossipIn) (GossipOut, error) { func JoinGossip(p2pCtx context.Context, self peer.ID, ps *pubsub.PubSub, log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, gossipIn GossipIn) (GossipOut, error) {
val := guardGossipValidator(log, logValidationResult(self, "validated block", log, BuildBlocksValidator(log, cfg, runCfg))) val := guardGossipValidator(log, logValidationResult(self, "validated block", log, BuildBlocksValidator(log, cfg, runCfg)))
blocksTopicName := blocksTopicV1(cfg) blocksTopicName := blocksTopicV1(cfg)
err := ps.RegisterTopicValidator(blocksTopicName, err := ps.RegisterTopicValidator(blocksTopicName,
...@@ -443,12 +442,6 @@ func JoinGossip(p2pCtx context.Context, self peer.ID, topicScoreParams *pubsub.T ...@@ -443,12 +442,6 @@ func JoinGossip(p2pCtx context.Context, self peer.ID, topicScoreParams *pubsub.T
} }
go LogTopicEvents(p2pCtx, log.New("topic", "blocks"), blocksTopicEvents) go LogTopicEvents(p2pCtx, log.New("topic", "blocks"), blocksTopicEvents)
if topicScoreParams != nil {
if err = blocksTopic.SetScoreParams(topicScoreParams); err != nil {
return nil, fmt.Errorf("failed to set topic score params: %w", err)
}
}
subscription, err := blocksTopic.Subscribe() subscription, err := blocksTopic.Subscribe()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to subscribe to blocks gossip topic: %w", err) return nil, fmt.Errorf("failed to subscribe to blocks gossip topic: %w", err)
......
...@@ -133,7 +133,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l ...@@ -133,7 +133,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
if err != nil { if err != nil {
return fmt.Errorf("failed to start gossipsub router: %w", err) return fmt.Errorf("failed to start gossipsub router: %w", err)
} }
n.gsOut, err = JoinGossip(resourcesCtx, n.host.ID(), setup.TopicScoringParams(), n.gs, log, rollupCfg, runCfg, gossipIn) n.gsOut, err = JoinGossip(resourcesCtx, n.host.ID(), n.gs, log, rollupCfg, runCfg, gossipIn)
if err != nil { if err != nil {
return fmt.Errorf("failed to join blocks gossip topic: %w", err) return fmt.Errorf("failed to join blocks gossip topic: %w", err)
} }
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"math" "math"
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/rollup"
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
) )
...@@ -12,6 +13,15 @@ import ( ...@@ -12,6 +13,15 @@ import (
// DecayToZero is the decay factor for a peer's score to zero. // DecayToZero is the decay factor for a peer's score to zero.
const DecayToZero = 0.01 const DecayToZero = 0.01
// MeshWeight is the weight of the mesh delivery topic.
const MeshWeight = -0.7
// MaxInMeshScore is the maximum score for being in the mesh.
const MaxInMeshScore = 10
// DecayEpoch is the number of epochs to decay the score over.
const DecayEpoch = time.Duration(5)
// ScoreDecay returns the decay factor for a given duration. // ScoreDecay returns the decay factor for a given duration.
func ScoreDecay(duration time.Duration, slot time.Duration) float64 { func ScoreDecay(duration time.Duration, slot time.Duration) float64 {
numOfTimes := duration / slot numOfTimes := duration / slot
...@@ -22,8 +32,8 @@ func ScoreDecay(duration time.Duration, slot time.Duration) float64 { ...@@ -22,8 +32,8 @@ func ScoreDecay(duration time.Duration, slot time.Duration) float64 {
// See [PeerScoreParams] for detailed documentation. // See [PeerScoreParams] for detailed documentation.
// //
// [PeerScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#PeerScoreParams // [PeerScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#PeerScoreParams
var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams { var LightPeerScoreParams = func(cfg *rollup.Config) pubsub.PeerScoreParams {
slot := time.Duration(blockTime) * time.Second slot := time.Duration(cfg.BlockTime) * time.Second
if slot == 0 { if slot == 0 {
slot = 2 * time.Second slot = 2 * time.Second
} }
...@@ -32,8 +42,29 @@ var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams { ...@@ -32,8 +42,29 @@ var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams {
epoch := 6 * slot epoch := 6 * slot
tenEpochs := 10 * epoch tenEpochs := 10 * epoch
oneHundredEpochs := 100 * epoch oneHundredEpochs := 100 * epoch
invalidDecayPeriod := 50 * epoch
return pubsub.PeerScoreParams{ return pubsub.PeerScoreParams{
Topics: make(map[string]*pubsub.TopicScoreParams), Topics: map[string]*pubsub.TopicScoreParams{
blocksTopicV1(cfg): {
TopicWeight: 0.8,
TimeInMeshWeight: MaxInMeshScore / inMeshCap(slot),
TimeInMeshQuantum: slot,
TimeInMeshCap: inMeshCap(slot),
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: ScoreDecay(20*epoch, slot),
FirstMessageDeliveriesCap: 23,
MeshMessageDeliveriesWeight: MeshWeight,
MeshMessageDeliveriesDecay: ScoreDecay(DecayEpoch*epoch, slot),
MeshMessageDeliveriesCap: float64(uint64(epoch/slot) * uint64(DecayEpoch)),
MeshMessageDeliveriesThreshold: float64(uint64(epoch/slot) * uint64(DecayEpoch) / 10),
MeshMessageDeliveriesWindow: 2 * time.Second,
MeshMessageDeliveriesActivation: 4 * epoch,
MeshFailurePenaltyWeight: MeshWeight,
MeshFailurePenaltyDecay: ScoreDecay(DecayEpoch*epoch, slot),
InvalidMessageDeliveriesWeight: -140.4475,
InvalidMessageDeliveriesDecay: ScoreDecay(invalidDecayPeriod, slot),
},
},
TopicScoreCap: 34, TopicScoreCap: 34,
AppSpecificScore: func(p peer.ID) float64 { AppSpecificScore: func(p peer.ID) float64 {
return 0 return 0
...@@ -51,65 +82,22 @@ var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams { ...@@ -51,65 +82,22 @@ var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams {
} }
} }
// DisabledPeerScoreParams is an instantiation of [pubsub.PeerScoreParams] where all scoring is disabled. // the cap for `inMesh` time scoring.
// See [PeerScoreParams] for detailed documentation. func inMeshCap(slot time.Duration) float64 {
// return float64((3600 * time.Second) / slot)
// [PeerScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#PeerScoreParams
var DisabledPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams {
slot := time.Duration(blockTime) * time.Second
if slot == 0 {
slot = 2 * time.Second
}
// We initialize an "epoch" as 6 blocks suggesting 6 blocks,
// each taking ~ 2 seconds, is 12 seconds
epoch := 6 * slot
tenEpochs := 10 * epoch
oneHundredEpochs := 100 * epoch
return pubsub.PeerScoreParams{
Topics: make(map[string]*pubsub.TopicScoreParams),
// 0 represent no cap
TopicScoreCap: 0,
AppSpecificScore: func(p peer.ID) float64 {
return 0
},
AppSpecificWeight: 1,
// ignore colocation scoring
IPColocationFactorWeight: 0,
IPColocationFactorWhitelist: nil,
// 0 disables the behaviour penalty
BehaviourPenaltyWeight: 0,
BehaviourPenaltyDecay: ScoreDecay(tenEpochs, slot),
DecayInterval: slot,
DecayToZero: DecayToZero,
RetainScore: oneHundredEpochs,
}
} }
// PeerScoreParamsByName is a map of name to function that returns a [pubsub.PeerScoreParams] based on the provided [rollup.Config]. func GetScoringParams(name string, cfg *rollup.Config) (*ScoringParams, error) {
var PeerScoreParamsByName = map[string](func(blockTime uint64) pubsub.PeerScoreParams){ switch name {
"light": LightPeerScoreParams, case "light":
"none": DisabledPeerScoreParams, return &ScoringParams{
} PeerScoring: LightPeerScoreParams(cfg),
}, nil
// AvailablePeerScoreParams returns a list of available peer score params. case "none":
// These can be used as an input to [GetPeerScoreParams] which returns the return nil, nil
// corresponding [pubsub.PeerScoreParams]. default:
func AvailablePeerScoreParams() []string { return nil, fmt.Errorf("unknown p2p scoring level: %v", name)
var params []string
for name := range PeerScoreParamsByName {
params = append(params, name)
}
return params
}
// GetPeerScoreParams returns the [pubsub.PeerScoreParams] for the given name.
func GetPeerScoreParams(name string, blockTime uint64) (pubsub.PeerScoreParams, error) {
params, ok := PeerScoreParamsByName[name]
if !ok {
return pubsub.PeerScoreParams{}, fmt.Errorf("invalid params %s", name)
} }
return params(blockTime), nil
} }
// NewPeerScoreThresholds returns a default [pubsub.PeerScoreThresholds]. // NewPeerScoreThresholds returns a default [pubsub.PeerScoreThresholds].
......
...@@ -2,10 +2,10 @@ package p2p ...@@ -2,10 +2,10 @@ package p2p
import ( import (
"math" "math"
"sort"
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
...@@ -25,14 +25,6 @@ func (testSuite *PeerParamsTestSuite) TestPeerScoreConstants() { ...@@ -25,14 +25,6 @@ func (testSuite *PeerParamsTestSuite) TestPeerScoreConstants() {
testSuite.Equal(0.01, DecayToZero) testSuite.Equal(0.01, DecayToZero)
} }
// TestAvailablePeerScoreParams validates the available peer score parameters.
func (testSuite *PeerParamsTestSuite) TestAvailablePeerScoreParams() {
available := AvailablePeerScoreParams()
sort.Strings(available)
expected := []string{"light", "none"}
testSuite.Equal(expected, available)
}
// TestNewPeerScoreThresholds validates the peer score thresholds. // TestNewPeerScoreThresholds validates the peer score thresholds.
// //
// This is tested to ensure that the thresholds are not modified and missed in review. // This is tested to ensure that the thresholds are not modified and missed in review.
...@@ -50,57 +42,17 @@ func (testSuite *PeerParamsTestSuite) TestNewPeerScoreThresholds() { ...@@ -50,57 +42,17 @@ func (testSuite *PeerParamsTestSuite) TestNewPeerScoreThresholds() {
} }
// TestGetPeerScoreParams validates the peer score parameters. // TestGetPeerScoreParams validates the peer score parameters.
func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams() { func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams_None() {
params, err := GetPeerScoreParams("light", 1) params, err := GetScoringParams("none", &chaincfg.Goerli)
testSuite.NoError(err) testSuite.NoError(err)
expected := LightPeerScoreParams(1) testSuite.Nil(params)
testSuite.Equal(expected.DecayInterval, params.DecayInterval)
testSuite.Equal(time.Duration(1)*time.Second, params.DecayInterval)
params, err = GetPeerScoreParams("none", 1)
testSuite.NoError(err)
expected = DisabledPeerScoreParams(1)
testSuite.Equal(expected.DecayInterval, params.DecayInterval)
testSuite.Equal(time.Duration(1)*time.Second, params.DecayInterval)
_, err = GetPeerScoreParams("invalid", 1)
testSuite.Error(err)
} }
// TestLightPeerScoreParams validates the light peer score params. // TestLightPeerScoreParams validates the light peer score params.
func (testSuite *PeerParamsTestSuite) TestLightPeerScoreParams() { func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams_Light() {
blockTime := uint64(1) cfg := chaincfg.Goerli
slot := time.Duration(blockTime) * time.Second cfg.BlockTime = 1
epoch := 6 * slot slot := time.Duration(cfg.BlockTime) * time.Second
oneHundredEpochs := 100 * epoch
// calculate the behavior penalty decay
duration := 10 * epoch
decay := math.Pow(DecayToZero, 1/float64(duration/slot))
testSuite.Equal(0.9261187281287935, decay)
// Test the params
params, err := GetPeerScoreParams("light", blockTime)
testSuite.NoError(err)
testSuite.Equal(params.Topics, make(map[string]*pubsub.TopicScoreParams))
testSuite.Equal(params.TopicScoreCap, float64(34))
// testSuite.Equal(params.AppSpecificScore("alice"), float(0))
testSuite.Equal(params.AppSpecificWeight, float64(1))
testSuite.Equal(params.IPColocationFactorWeight, float64(-35))
testSuite.Equal(params.IPColocationFactorThreshold, int(10))
testSuite.Nil(params.IPColocationFactorWhitelist)
testSuite.Equal(params.BehaviourPenaltyWeight, float64(-16))
testSuite.Equal(params.BehaviourPenaltyThreshold, float64(6))
testSuite.Equal(params.BehaviourPenaltyDecay, decay)
testSuite.Equal(params.DecayInterval, slot)
testSuite.Equal(params.DecayToZero, DecayToZero)
testSuite.Equal(params.RetainScore, oneHundredEpochs)
}
// TestDisabledPeerScoreParams validates the disabled peer score params.
func (testSuite *PeerParamsTestSuite) TestDisabledPeerScoreParams() {
blockTime := uint64(1)
slot := time.Duration(blockTime) * time.Second
epoch := 6 * slot epoch := 6 * slot
oneHundredEpochs := 100 * epoch oneHundredEpochs := 100 * epoch
...@@ -110,27 +62,33 @@ func (testSuite *PeerParamsTestSuite) TestDisabledPeerScoreParams() { ...@@ -110,27 +62,33 @@ func (testSuite *PeerParamsTestSuite) TestDisabledPeerScoreParams() {
testSuite.Equal(0.9261187281287935, decay) testSuite.Equal(0.9261187281287935, decay)
// Test the params // Test the params
params, err := GetPeerScoreParams("none", blockTime) scoringParams, err := GetScoringParams("light", &cfg)
peerParams := scoringParams.PeerScoring
testSuite.NoError(err) testSuite.NoError(err)
testSuite.Equal(params.Topics, make(map[string]*pubsub.TopicScoreParams)) // Topics should contain options for block topic
testSuite.Equal(params.TopicScoreCap, float64(0)) testSuite.Len(peerParams.Topics, 1)
testSuite.Equal(params.AppSpecificWeight, float64(1)) topicParams, ok := peerParams.Topics[blocksTopicV1(&cfg)]
testSuite.Equal(params.IPColocationFactorWeight, float64(0)) testSuite.True(ok, "should have block topic params")
testSuite.Nil(params.IPColocationFactorWhitelist) testSuite.NotZero(topicParams.TimeInMeshQuantum)
testSuite.Equal(params.BehaviourPenaltyWeight, float64(0)) testSuite.Equal(peerParams.TopicScoreCap, float64(34))
testSuite.Equal(params.BehaviourPenaltyDecay, decay) testSuite.Equal(peerParams.AppSpecificWeight, float64(1))
testSuite.Equal(params.DecayInterval, slot) testSuite.Equal(peerParams.IPColocationFactorWeight, float64(-35))
testSuite.Equal(params.DecayToZero, DecayToZero) testSuite.Equal(peerParams.IPColocationFactorThreshold, 10)
testSuite.Equal(params.RetainScore, oneHundredEpochs) testSuite.Nil(peerParams.IPColocationFactorWhitelist)
testSuite.Equal(peerParams.BehaviourPenaltyWeight, float64(-16))
testSuite.Equal(peerParams.BehaviourPenaltyThreshold, float64(6))
testSuite.Equal(peerParams.BehaviourPenaltyDecay, decay)
testSuite.Equal(peerParams.DecayInterval, slot)
testSuite.Equal(peerParams.DecayToZero, DecayToZero)
testSuite.Equal(peerParams.RetainScore, oneHundredEpochs)
} }
// TestParamsZeroBlockTime validates peer score params use default slot for 0 block time. // TestParamsZeroBlockTime validates peer score params use default slot for 0 block time.
func (testSuite *PeerParamsTestSuite) TestParamsZeroBlockTime() { func (testSuite *PeerParamsTestSuite) TestParamsZeroBlockTime() {
cfg := chaincfg.Goerli
cfg.BlockTime = 0
slot := 2 * time.Second slot := 2 * time.Second
params, err := GetPeerScoreParams("none", uint64(0)) params, err := GetScoringParams("light", &cfg)
testSuite.NoError(err)
testSuite.Equal(params.DecayInterval, slot)
params, err = GetPeerScoreParams("light", uint64(0))
testSuite.NoError(err) testSuite.NoError(err)
testSuite.Equal(params.DecayInterval, slot) testSuite.Equal(params.PeerScoring.DecayInterval, slot)
} }
...@@ -102,17 +102,19 @@ func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts [] ...@@ -102,17 +102,19 @@ func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts []
&rollup.Config{L2ChainID: big.NewInt(123)}, &rollup.Config{L2ChainID: big.NewInt(123)},
extPeerStore, testSuite.mockMetricer, logger) extPeerStore, testSuite.mockMetricer, logger)
opts = append(opts, ConfigurePeerScoring(&Config{ opts = append(opts, ConfigurePeerScoring(&Config{
PeerScoring: &pubsub.PeerScoreParams{ ScoringParams: &ScoringParams{
AppSpecificScore: func(p peer.ID) float64 { PeerScoring: pubsub.PeerScoreParams{
if p == hosts[0].ID() { AppSpecificScore: func(p peer.ID) float64 {
return -1000 if p == hosts[0].ID() {
} else { return -1000
return 0 } else {
} return 0
}
},
AppSpecificWeight: 1,
DecayInterval: time.Second,
DecayToZero: 0.01,
}, },
AppSpecificWeight: 1,
DecayInterval: time.Second,
DecayToZero: 0.01,
}, },
}, scorer, logger)...) }, scorer, logger)...)
ps, err := pubsub.NewGossipSubWithRouter(ctx, h, rt, opts...) ps, err := pubsub.NewGossipSubWithRouter(ctx, h, rt, opts...)
......
...@@ -85,10 +85,6 @@ func (p *Prepared) BanDuration() time.Duration { ...@@ -85,10 +85,6 @@ func (p *Prepared) BanDuration() time.Duration {
return 1 * time.Hour return 1 * time.Hour
} }
func (p *Prepared) TopicScoringParams() *pubsub.TopicScoreParams {
return nil
}
func (p *Prepared) Disabled() bool { func (p *Prepared) Disabled() bool {
return false return false
} }
......
package p2p
import (
"fmt"
"time"
pubsub "github.com/libp2p/go-libp2p-pubsub"
)
// MeshWeight is the weight of the mesh delivery topic.
const MeshWeight = -0.7
// MaxInMeshScore is the maximum score for being in the mesh.
const MaxInMeshScore = 10
// DecayEpoch is the number of epochs to decay the score over.
const DecayEpoch = time.Duration(5)
// LightTopicScoreParams is a default instantiation of [pubsub.TopicScoreParams].
// See [TopicScoreParams] for detailed documentation.
//
// [TopicScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#TopicScoreParams
var LightTopicScoreParams = func(blockTime uint64) pubsub.TopicScoreParams {
slot := time.Duration(blockTime) * time.Second
if slot == 0 {
slot = 2 * time.Second
}
epoch := 6 * slot
invalidDecayPeriod := 50 * epoch
return pubsub.TopicScoreParams{
TopicWeight: 0.8,
TimeInMeshWeight: MaxInMeshScore / inMeshCap(slot),
TimeInMeshQuantum: slot,
TimeInMeshCap: inMeshCap(slot),
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: ScoreDecay(20*epoch, slot),
FirstMessageDeliveriesCap: 23,
MeshMessageDeliveriesWeight: MeshWeight,
MeshMessageDeliveriesDecay: ScoreDecay(DecayEpoch*epoch, slot),
MeshMessageDeliveriesCap: float64(uint64(epoch/slot) * uint64(DecayEpoch)),
MeshMessageDeliveriesThreshold: float64(uint64(epoch/slot) * uint64(DecayEpoch) / 10),
MeshMessageDeliveriesWindow: 2 * time.Second,
MeshMessageDeliveriesActivation: 4 * epoch,
MeshFailurePenaltyWeight: MeshWeight,
MeshFailurePenaltyDecay: ScoreDecay(DecayEpoch*epoch, slot),
InvalidMessageDeliveriesWeight: -140.4475,
InvalidMessageDeliveriesDecay: ScoreDecay(invalidDecayPeriod, slot),
}
}
// the cap for `inMesh` time scoring.
func inMeshCap(slot time.Duration) float64 {
return float64((3600 * time.Second) / slot)
}
// DisabledTopicScoreParams is an instantiation of [pubsub.TopicScoreParams] where all scoring is disabled.
// See [TopicScoreParams] for detailed documentation.
//
// [TopicScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#TopicScoreParams
var DisabledTopicScoreParams = func(blockTime uint64) pubsub.TopicScoreParams {
slot := time.Duration(blockTime) * time.Second
if slot == 0 {
slot = 2 * time.Second
}
epoch := 6 * slot
invalidDecayPeriod := 50 * epoch
return pubsub.TopicScoreParams{
TopicWeight: 0, // disabled
TimeInMeshWeight: 0, // disabled
TimeInMeshQuantum: slot,
TimeInMeshCap: inMeshCap(slot),
FirstMessageDeliveriesWeight: 0, // disabled
FirstMessageDeliveriesDecay: ScoreDecay(20*epoch, slot),
FirstMessageDeliveriesCap: 23,
MeshMessageDeliveriesWeight: 0, // disabled
MeshMessageDeliveriesDecay: ScoreDecay(DecayEpoch*epoch, slot),
MeshMessageDeliveriesCap: float64(uint64(epoch/slot) * uint64(DecayEpoch)),
MeshMessageDeliveriesThreshold: float64(uint64(epoch/slot) * uint64(DecayEpoch) / 10),
MeshMessageDeliveriesWindow: 2 * time.Second,
MeshMessageDeliveriesActivation: 4 * epoch,
MeshFailurePenaltyWeight: 0, // disabled
MeshFailurePenaltyDecay: ScoreDecay(DecayEpoch*epoch, slot),
InvalidMessageDeliveriesWeight: 0, // disabled
InvalidMessageDeliveriesDecay: ScoreDecay(invalidDecayPeriod, slot),
}
}
// TopicScoreParamsByName is a map of name to [pubsub.TopicScoreParams].
var TopicScoreParamsByName = map[string](func(blockTime uint64) pubsub.TopicScoreParams){
"light": LightTopicScoreParams,
"none": DisabledTopicScoreParams,
}
// AvailableTopicScoreParams returns a list of available topic score params.
// These can be used as an input to [GetTopicScoreParams] which returns the
// corresponding [pubsub.TopicScoreParams].
func AvailableTopicScoreParams() []string {
var params []string
for name := range TopicScoreParamsByName {
params = append(params, name)
}
return params
}
// GetTopicScoreParams returns the [pubsub.TopicScoreParams] for the given name.
func GetTopicScoreParams(name string, blockTime uint64) (pubsub.TopicScoreParams, error) {
params, ok := TopicScoreParamsByName[name]
if !ok {
return pubsub.TopicScoreParams{}, fmt.Errorf("invalid topic params %s", name)
}
return params(blockTime), nil
}
...@@ -43,7 +43,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -43,7 +43,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
return nil, fmt.Errorf("failed to load p2p signer: %w", err) return nil, fmt.Errorf("failed to load p2p signer: %w", err)
} }
p2pConfig, err := p2pcli.NewConfig(ctx, rollupConfig.BlockTime) p2pConfig, err := p2pcli.NewConfig(ctx, rollupConfig)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load p2p config: %w", err) return nil, fmt.Errorf("failed to load p2p config: %w", err)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment