github.com/ethereum-optimism/optimism@v1.7.2/op-node/p2p/app_scores_test.go (about) 1 package p2p 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/ethereum-optimism/optimism/op-node/p2p/store" 10 "github.com/ethereum-optimism/optimism/op-service/clock" 11 "github.com/ethereum-optimism/optimism/op-service/testlog" 12 "github.com/ethereum/go-ethereum/log" 13 "github.com/libp2p/go-libp2p/core/peer" 14 "github.com/stretchr/testify/require" 15 ) 16 17 type stubScoreBookUpdate struct { 18 id peer.ID 19 diff store.ScoreDiff 20 } 21 type stubScoreBook struct { 22 err error 23 scores map[peer.ID]store.PeerScores 24 updates chan stubScoreBookUpdate 25 } 26 27 func (s *stubScoreBook) GetPeerScores(id peer.ID) (store.PeerScores, error) { 28 if s.err != nil { 29 return store.PeerScores{}, s.err 30 } 31 scores, ok := s.scores[id] 32 if !ok { 33 return store.PeerScores{}, nil 34 } 35 return scores, nil 36 } 37 38 func (s *stubScoreBook) SetScore(id peer.ID, diff store.ScoreDiff) (store.PeerScores, error) { 39 s.updates <- stubScoreBookUpdate{id, diff} 40 return s.GetPeerScores(id) 41 } 42 43 type appScoreTestData struct { 44 ctx context.Context 45 logger log.Logger 46 clock *clock.DeterministicClock 47 peers []peer.ID 48 scorebook *stubScoreBook 49 } 50 51 func (a *appScoreTestData) WaitForNextScoreBookUpdate(t *testing.T) stubScoreBookUpdate { 52 ctx, cancelFunc := context.WithTimeout(a.ctx, 30*time.Second) 53 defer cancelFunc() 54 select { 55 case update := <-a.scorebook.updates: 56 return update 57 case <-ctx.Done(): 58 t.Fatal("Did not receive expected scorebook update") 59 return stubScoreBookUpdate{} 60 } 61 } 62 63 func setupPeerApplicationScorerTest(t *testing.T, params *ApplicationScoreParams) (*appScoreTestData, *peerApplicationScorer) { 64 data := &appScoreTestData{ 65 ctx: context.Background(), 66 logger: testlog.Logger(t, log.LevelInfo), 67 clock: clock.NewDeterministicClock(time.UnixMilli(1000)), 68 peers: []peer.ID{}, 69 scorebook: &stubScoreBook{ 70 scores: make(map[peer.ID]store.PeerScores), 71 updates: make(chan stubScoreBookUpdate, 10), 72 }, 73 } 74 appScorer := newPeerApplicationScorer(data.ctx, data.logger, data.clock, params, data.scorebook, func() []peer.ID { 75 return data.peers 76 }) 77 return data, appScorer 78 } 79 80 func TestIncrementValidResponses(t *testing.T) { 81 data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{ 82 ValidResponseCap: 10, 83 }) 84 85 appScorer.onValidResponse("aaa") 86 require.Len(t, data.scorebook.updates, 1) 87 update := <-data.scorebook.updates 88 require.Equal(t, stubScoreBookUpdate{peer.ID("aaa"), store.IncrementValidResponses{Cap: 10}}, update) 89 } 90 91 func TestIncrementErrorResponses(t *testing.T) { 92 data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{ 93 ErrorResponseCap: 10, 94 }) 95 96 appScorer.onResponseError("aaa") 97 require.Len(t, data.scorebook.updates, 1) 98 update := <-data.scorebook.updates 99 require.Equal(t, stubScoreBookUpdate{peer.ID("aaa"), store.IncrementErrorResponses{Cap: 10}}, update) 100 } 101 102 func TestIncrementRejectedPayloads(t *testing.T) { 103 data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{ 104 RejectedPayloadCap: 10, 105 }) 106 107 appScorer.onRejectedPayload("aaa") 108 require.Len(t, data.scorebook.updates, 1) 109 update := <-data.scorebook.updates 110 require.Equal(t, stubScoreBookUpdate{peer.ID("aaa"), store.IncrementRejectedPayloads{Cap: 10}}, update) 111 } 112 113 func TestApplicationScore(t *testing.T) { 114 data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{ 115 ValidResponseWeight: 0.8, 116 ErrorResponseWeight: 0.6, 117 RejectedPayloadWeight: 0.4, 118 }) 119 120 peerScore := store.PeerScores{ 121 ReqResp: store.ReqRespScores{ 122 ValidResponses: 1, 123 ErrorResponses: 2, 124 RejectedPayloads: 3, 125 }, 126 } 127 data.scorebook.scores["aaa"] = peerScore 128 score := appScorer.ApplicationScore("aaa") 129 require.Equal(t, 1*0.8+2*0.6+3*0.4, score) 130 } 131 132 func TestApplicationScoreZeroWhenScoreDoesNotLoad(t *testing.T) { 133 data, appScorer := setupPeerApplicationScorerTest(t, &ApplicationScoreParams{}) 134 135 data.scorebook.err = errors.New("boom") 136 score := appScorer.ApplicationScore("aaa") 137 require.Zero(t, score) 138 } 139 140 func TestDecayScoresAfterDecayInterval(t *testing.T) { 141 params := &ApplicationScoreParams{ 142 ValidResponseDecay: 0.8, 143 ErrorResponseDecay: 0.7, 144 RejectedPayloadDecay: 0.3, 145 DecayToZero: 0.1, 146 DecayInterval: 90 * time.Second, 147 } 148 data, appScorer := setupPeerApplicationScorerTest(t, params) 149 data.peers = []peer.ID{"aaa", "bbb"} 150 151 expectedDecay := &store.DecayApplicationScores{ 152 ValidResponseDecay: 0.8, 153 ErrorResponseDecay: 0.7, 154 RejectedPayloadDecay: 0.3, 155 DecayToZero: 0.1, 156 } 157 158 appScorer.start() 159 defer appScorer.stop() 160 161 data.clock.WaitForNewPendingTaskWithTimeout(30 * time.Second) 162 163 data.clock.AdvanceTime(params.DecayInterval) 164 165 require.Equal(t, stubScoreBookUpdate{id: "aaa", diff: expectedDecay}, data.WaitForNextScoreBookUpdate(t)) 166 require.Equal(t, stubScoreBookUpdate{id: "bbb", diff: expectedDecay}, data.WaitForNextScoreBookUpdate(t)) 167 }