github.com/amazechain/amc@v0.1.3/internal/p2p/gossip_scoring_params.go (about)

     1  package p2p
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"reflect"
     7  	"strings"
     8  	"time"
     9  
    10  	pubsub "github.com/libp2p/go-libp2p-pubsub"
    11  	"github.com/libp2p/go-libp2p/core/peer"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  const (
    16  	// beaconBlockWeight specifies the scoring weight that we apply to
    17  	// our beacon block topic.
    18  	beaconBlockWeight = 0.8
    19  	// aggregateWeight specifies the scoring weight that we apply to
    20  	// our aggregate topic.
    21  	aggregateWeight = 0.5
    22  	// syncContributionWeight specifies the scoring weight that we apply to
    23  	// our sync contribution topic.
    24  	syncContributionWeight = 0.2
    25  	// attestationTotalWeight specifies the scoring weight that we apply to
    26  	// our attestation subnet topic.
    27  	attestationTotalWeight = 1
    28  	// syncCommitteesTotalWeight specifies the scoring weight that we apply to
    29  	// our sync subnet topic.
    30  	syncCommitteesTotalWeight = 0.4
    31  	// attesterSlashingWeight specifies the scoring weight that we apply to
    32  	// our attester slashing topic.
    33  	attesterSlashingWeight = 0.05
    34  	// proposerSlashingWeight specifies the scoring weight that we apply to
    35  	// our proposer slashing topic.
    36  	proposerSlashingWeight = 0.05
    37  	// voluntaryExitWeight specifies the scoring weight that we apply to
    38  	// our voluntary exit topic.
    39  	voluntaryExitWeight = 0.05
    40  	// blsToExecutionChangeWeight specifies the scoring weight that we apply to
    41  	// our bls to execution topic.
    42  	blsToExecutionChangeWeight = 0.05
    43  
    44  	// maxInMeshScore describes the max score a peer can attain from being in the mesh.
    45  	maxInMeshScore = 10
    46  	// maxFirstDeliveryScore describes the max score a peer can obtain from first deliveries.
    47  	maxFirstDeliveryScore = 40
    48  
    49  	// decayToZero specifies the terminal value that we will use when decaying
    50  	// a value.
    51  	decayToZero = 0.01
    52  
    53  	// dampeningFactor reduces the amount by which the various thresholds and caps are created.
    54  	dampeningFactor = 90
    55  )
    56  
    57  var (
    58  	// a bool to check if we enable scoring for messages in the mesh sent for near first deliveries.
    59  	meshDeliveryIsScored = false
    60  
    61  	// Defines the variables representing the different time periods.
    62  	oneHundredBlocks   = 100 * oneBlockDuration()
    63  	invalidDecayPeriod = 50 * oneBlockDuration()
    64  	twentyBlocks       = 20 * oneBlockDuration()
    65  	tenBlocks          = 10 * oneBlockDuration()
    66  )
    67  
    68  func peerScoringParams() (*pubsub.PeerScoreParams, *pubsub.PeerScoreThresholds) {
    69  	thresholds := &pubsub.PeerScoreThresholds{
    70  		GossipThreshold:             -4000,
    71  		PublishThreshold:            -8000,
    72  		GraylistThreshold:           -16000,
    73  		AcceptPXThreshold:           100,
    74  		OpportunisticGraftThreshold: 5,
    75  	}
    76  	scoreParams := &pubsub.PeerScoreParams{
    77  		Topics:        make(map[string]*pubsub.TopicScoreParams),
    78  		TopicScoreCap: 32.72,
    79  		AppSpecificScore: func(p peer.ID) float64 {
    80  			return 0
    81  		},
    82  		AppSpecificWeight:           1,
    83  		IPColocationFactorWeight:    -35.11,
    84  		IPColocationFactorThreshold: 10,
    85  		IPColocationFactorWhitelist: nil,
    86  		BehaviourPenaltyWeight:      -15.92,
    87  		BehaviourPenaltyThreshold:   6,
    88  		BehaviourPenaltyDecay:       scoreDecay(tenBlocks),
    89  		DecayInterval:               oneBlockDuration(),
    90  		DecayToZero:                 decayToZero,
    91  		RetainScore:                 oneHundredBlocks,
    92  	}
    93  	return scoreParams, thresholds
    94  }
    95  
    96  func (s *Service) topicScoreParams(topic string) (*pubsub.TopicScoreParams, error) {
    97  	switch {
    98  	case strings.Contains(topic, GossipBlockMessage):
    99  		return defaultBlockTopicParams(), nil
   100  	case strings.Contains(topic, GossipExitMessage):
   101  		return defaultVoluntaryExitTopicParams(), nil
   102  	default:
   103  		return nil, errors.Errorf("unrecognized topic provided for parameter registration: %s", topic)
   104  	}
   105  }
   106  
   107  // Based on the lighthouse parameters.
   108  // https://gist.github.com/blacktemplar/5c1862cb3f0e32a1a7fb0b25e79e6e2c
   109  
   110  func defaultBlockTopicParams() *pubsub.TopicScoreParams {
   111  	//todo
   112  	decayEpoch := time.Duration(5)
   113  	meshWeight := -0.717
   114  	if !meshDeliveryIsScored {
   115  		// Set the mesh weight as zero as a temporary measure, so as to prevent
   116  		// the average nodes from being penalised.
   117  		meshWeight = 0
   118  	}
   119  	return &pubsub.TopicScoreParams{
   120  		TopicWeight:                     beaconBlockWeight,
   121  		TimeInMeshWeight:                maxInMeshScore / inMeshCap(),
   122  		TimeInMeshQuantum:               inMeshTime(),
   123  		TimeInMeshCap:                   inMeshCap(),
   124  		FirstMessageDeliveriesWeight:    1,
   125  		FirstMessageDeliveriesDecay:     scoreDecay(twentyBlocks),
   126  		FirstMessageDeliveriesCap:       23,
   127  		MeshMessageDeliveriesWeight:     meshWeight,
   128  		MeshMessageDeliveriesDecay:      scoreDecay(decayEpoch * oneBlockDuration()),
   129  		MeshMessageDeliveriesCap:        float64(decayEpoch),
   130  		MeshMessageDeliveriesThreshold:  float64(decayEpoch) / 10,
   131  		MeshMessageDeliveriesWindow:     2 * time.Second,
   132  		MeshMessageDeliveriesActivation: 4 * oneBlockDuration(),
   133  		MeshFailurePenaltyWeight:        meshWeight,
   134  		MeshFailurePenaltyDecay:         scoreDecay(decayEpoch * oneBlockDuration()),
   135  		InvalidMessageDeliveriesWeight:  -140.4475,
   136  		InvalidMessageDeliveriesDecay:   scoreDecay(invalidDecayPeriod),
   137  	}
   138  }
   139  
   140  func defaultSyncContributionTopicParams() *pubsub.TopicScoreParams {
   141  	// Determine the expected message rate for the particular gossip topic.
   142  	//todo
   143  	aggPerSlot := 2
   144  	firstMessageCap, err := decayLimit(scoreDecay(1*oneBlockDuration()), float64(aggPerSlot*2/gossipSubD))
   145  	if err != nil {
   146  		log.Warn("skipping initializing topic scoring", "err", err)
   147  		return nil
   148  	}
   149  	firstMessageWeight := maxFirstDeliveryScore / firstMessageCap
   150  	meshThreshold, err := decayThreshold(scoreDecay(1*oneBlockDuration()), float64(aggPerSlot)/dampeningFactor)
   151  	if err != nil {
   152  		log.Warn("skipping initializing topic scoring", "err", err)
   153  		return nil
   154  	}
   155  	meshWeight := -scoreByWeight(syncContributionWeight, meshThreshold)
   156  	meshCap := 4 * meshThreshold
   157  	if !meshDeliveryIsScored {
   158  		// Set the mesh weight as zero as a temporary measure, so as to prevent
   159  		// the average nodes from being penalised.
   160  		meshWeight = 0
   161  	}
   162  	return &pubsub.TopicScoreParams{
   163  		TopicWeight:                     syncContributionWeight,
   164  		TimeInMeshWeight:                maxInMeshScore / inMeshCap(),
   165  		TimeInMeshQuantum:               inMeshTime(),
   166  		TimeInMeshCap:                   inMeshCap(),
   167  		FirstMessageDeliveriesWeight:    firstMessageWeight,
   168  		FirstMessageDeliveriesDecay:     scoreDecay(1 * oneBlockDuration()),
   169  		FirstMessageDeliveriesCap:       firstMessageCap,
   170  		MeshMessageDeliveriesWeight:     meshWeight,
   171  		MeshMessageDeliveriesDecay:      scoreDecay(1 * oneBlockDuration()),
   172  		MeshMessageDeliveriesCap:        meshCap,
   173  		MeshMessageDeliveriesThreshold:  meshThreshold,
   174  		MeshMessageDeliveriesWindow:     2 * time.Second,
   175  		MeshMessageDeliveriesActivation: 1 * oneBlockDuration(),
   176  		MeshFailurePenaltyWeight:        meshWeight,
   177  		MeshFailurePenaltyDecay:         scoreDecay(1 * oneBlockDuration()),
   178  		InvalidMessageDeliveriesWeight:  -maxScore() / syncContributionWeight,
   179  		InvalidMessageDeliveriesDecay:   scoreDecay(invalidDecayPeriod),
   180  	}
   181  }
   182  
   183  func defaultAttesterSlashingTopicParams() *pubsub.TopicScoreParams {
   184  	return &pubsub.TopicScoreParams{
   185  		TopicWeight:                     attesterSlashingWeight,
   186  		TimeInMeshWeight:                maxInMeshScore / inMeshCap(),
   187  		TimeInMeshQuantum:               inMeshTime(),
   188  		TimeInMeshCap:                   inMeshCap(),
   189  		FirstMessageDeliveriesWeight:    36,
   190  		FirstMessageDeliveriesDecay:     scoreDecay(oneHundredBlocks),
   191  		FirstMessageDeliveriesCap:       1,
   192  		MeshMessageDeliveriesWeight:     0,
   193  		MeshMessageDeliveriesDecay:      0,
   194  		MeshMessageDeliveriesCap:        0,
   195  		MeshMessageDeliveriesThreshold:  0,
   196  		MeshMessageDeliveriesWindow:     0,
   197  		MeshMessageDeliveriesActivation: 0,
   198  		MeshFailurePenaltyWeight:        0,
   199  		MeshFailurePenaltyDecay:         0,
   200  		InvalidMessageDeliveriesWeight:  -2000,
   201  		InvalidMessageDeliveriesDecay:   scoreDecay(invalidDecayPeriod),
   202  	}
   203  }
   204  
   205  func defaultProposerSlashingTopicParams() *pubsub.TopicScoreParams {
   206  	return &pubsub.TopicScoreParams{
   207  		TopicWeight:                     proposerSlashingWeight,
   208  		TimeInMeshWeight:                maxInMeshScore / inMeshCap(),
   209  		TimeInMeshQuantum:               inMeshTime(),
   210  		TimeInMeshCap:                   inMeshCap(),
   211  		FirstMessageDeliveriesWeight:    36,
   212  		FirstMessageDeliveriesDecay:     scoreDecay(oneHundredBlocks),
   213  		FirstMessageDeliveriesCap:       1,
   214  		MeshMessageDeliveriesWeight:     0,
   215  		MeshMessageDeliveriesDecay:      0,
   216  		MeshMessageDeliveriesCap:        0,
   217  		MeshMessageDeliveriesThreshold:  0,
   218  		MeshMessageDeliveriesWindow:     0,
   219  		MeshMessageDeliveriesActivation: 0,
   220  		MeshFailurePenaltyWeight:        0,
   221  		MeshFailurePenaltyDecay:         0,
   222  		InvalidMessageDeliveriesWeight:  -2000,
   223  		InvalidMessageDeliveriesDecay:   scoreDecay(invalidDecayPeriod),
   224  	}
   225  }
   226  
   227  func defaultVoluntaryExitTopicParams() *pubsub.TopicScoreParams {
   228  	return &pubsub.TopicScoreParams{
   229  		TopicWeight:                     voluntaryExitWeight,
   230  		TimeInMeshWeight:                maxInMeshScore / inMeshCap(),
   231  		TimeInMeshQuantum:               inMeshTime(),
   232  		TimeInMeshCap:                   inMeshCap(),
   233  		FirstMessageDeliveriesWeight:    2,
   234  		FirstMessageDeliveriesDecay:     scoreDecay(oneHundredBlocks),
   235  		FirstMessageDeliveriesCap:       5,
   236  		MeshMessageDeliveriesWeight:     0,
   237  		MeshMessageDeliveriesDecay:      0,
   238  		MeshMessageDeliveriesCap:        0,
   239  		MeshMessageDeliveriesThreshold:  0,
   240  		MeshMessageDeliveriesWindow:     0,
   241  		MeshMessageDeliveriesActivation: 0,
   242  		MeshFailurePenaltyWeight:        0,
   243  		MeshFailurePenaltyDecay:         0,
   244  		InvalidMessageDeliveriesWeight:  -2000,
   245  		InvalidMessageDeliveriesDecay:   scoreDecay(invalidDecayPeriod),
   246  	}
   247  }
   248  
   249  func defaultBlsToExecutionChangeTopicParams() *pubsub.TopicScoreParams {
   250  	return &pubsub.TopicScoreParams{
   251  		TopicWeight:                     blsToExecutionChangeWeight,
   252  		TimeInMeshWeight:                maxInMeshScore / inMeshCap(),
   253  		TimeInMeshQuantum:               inMeshTime(),
   254  		TimeInMeshCap:                   inMeshCap(),
   255  		FirstMessageDeliveriesWeight:    2,
   256  		FirstMessageDeliveriesDecay:     scoreDecay(oneHundredBlocks),
   257  		FirstMessageDeliveriesCap:       5,
   258  		MeshMessageDeliveriesWeight:     0,
   259  		MeshMessageDeliveriesDecay:      0,
   260  		MeshMessageDeliveriesCap:        0,
   261  		MeshMessageDeliveriesThreshold:  0,
   262  		MeshMessageDeliveriesWindow:     0,
   263  		MeshMessageDeliveriesActivation: 0,
   264  		MeshFailurePenaltyWeight:        0,
   265  		MeshFailurePenaltyDecay:         0,
   266  		InvalidMessageDeliveriesWeight:  -2000,
   267  		InvalidMessageDeliveriesDecay:   scoreDecay(invalidDecayPeriod),
   268  	}
   269  }
   270  
   271  func oneBlockDuration() time.Duration {
   272  	//todo
   273  	return 8 * time.Second
   274  }
   275  
   276  // determines the decay rate from the provided time period till
   277  // the decayToZero value. Ex: ( 1 -> 0.01)
   278  func scoreDecay(totalDurationDecay time.Duration) float64 {
   279  	numOfTimes := totalDurationDecay / oneBlockDuration()
   280  	return math.Pow(decayToZero, 1/float64(numOfTimes))
   281  }
   282  
   283  // is used to determine the threshold from the decay limit with
   284  // a provided growth rate. This applies the decay rate to a
   285  // computed limit.
   286  func decayThreshold(decayRate, rate float64) (float64, error) {
   287  	d, err := decayLimit(decayRate, rate)
   288  	if err != nil {
   289  		return 0, err
   290  	}
   291  	return d * decayRate, nil
   292  }
   293  
   294  // decayLimit provides the value till which a decay process will
   295  // limit till provided with an expected growth rate.
   296  func decayLimit(decayRate, rate float64) (float64, error) {
   297  	if 1 <= decayRate {
   298  		return 0, errors.Errorf("got an invalid decayLimit rate: %f", decayRate)
   299  	}
   300  	return rate / (1 - decayRate), nil
   301  }
   302  
   303  // provides the relevant score by the provided weight and threshold.
   304  func scoreByWeight(weight, threshold float64) float64 {
   305  	return maxScore() / (weight * threshold * threshold)
   306  }
   307  
   308  // maxScore attainable by a peer.
   309  func maxScore() float64 {
   310  	totalWeight := beaconBlockWeight + aggregateWeight + syncContributionWeight +
   311  		attestationTotalWeight + syncCommitteesTotalWeight + attesterSlashingWeight +
   312  		proposerSlashingWeight + voluntaryExitWeight + blsToExecutionChangeWeight
   313  	return (maxInMeshScore + maxFirstDeliveryScore) * totalWeight
   314  }
   315  
   316  // denotes the unit time in mesh for scoring tallying.
   317  func inMeshTime() time.Duration {
   318  	return 1 * oneBlockDuration()
   319  }
   320  
   321  // the cap for `inMesh` time scoring.
   322  func inMeshCap() float64 {
   323  	return float64((3600 * time.Second) / inMeshTime())
   324  }
   325  
   326  func logGossipParameters(topic string, params *pubsub.TopicScoreParams) {
   327  	// Exit early in the event the parameter struct is nil.
   328  	if params == nil {
   329  		return
   330  	}
   331  	rawParams := reflect.ValueOf(params).Elem()
   332  	numOfFields := rawParams.NumField()
   333  
   334  	fields := make([]interface{}, numOfFields*2)
   335  	for i := 0; i < numOfFields; i++ {
   336  		fields = append(fields, reflect.TypeOf(params).Elem().Field(i).Name, rawParams.Field(i).Interface())
   337  	}
   338  	log.Debug(fmt.Sprintf("Topic Parameters for %s", topic), fields...)
   339  }