Commit 8bec48bb authored by Adrian Sutton's avatar Adrian Sutton

op-node: Add peer score diffs to increment and decay application scores

parent e8cdf4e4
...@@ -2,6 +2,7 @@ package store ...@@ -2,6 +2,7 @@ package store
import ( import (
"errors" "errors"
"math"
"net" "net"
"time" "time"
...@@ -27,9 +28,59 @@ func (g GossipScores) Apply(rec *scoreRecord) { ...@@ -27,9 +28,59 @@ func (g GossipScores) Apply(rec *scoreRecord) {
rec.PeerScores.Gossip = g rec.PeerScores.Gossip = g
} }
type ReqRespScores struct {
ValidResponses float64 `json:"validResponses"`
ErrorResponses float64 `json:"errorResponses"`
RejectedPayloads float64 `json:"rejectedPayloads"`
}
type IncrementValidResponses struct {
Cap float64
}
func (i IncrementValidResponses) Apply(rec *scoreRecord) {
rec.PeerScores.ReqResp.ValidResponses = math.Min(rec.PeerScores.ReqResp.ValidResponses+1, i.Cap)
}
type IncrementErrorResponses struct {
Cap float64
}
func (i IncrementErrorResponses) Apply(rec *scoreRecord) {
rec.PeerScores.ReqResp.ErrorResponses = math.Min(rec.PeerScores.ReqResp.ErrorResponses+1, i.Cap)
}
type IncrementRejectedPayloads struct {
Cap float64
}
func (i IncrementRejectedPayloads) Apply(rec *scoreRecord) {
rec.PeerScores.ReqResp.RejectedPayloads = math.Min(rec.PeerScores.ReqResp.RejectedPayloads+1, i.Cap)
}
type DecayApplicationScores struct {
ValidResponseDecay float64
ErrorResponseDecay float64
RejectedPayloadDecay float64
DecayToZero float64
}
func (d *DecayApplicationScores) Apply(rec *scoreRecord) {
decay := func(value float64, decay float64) float64 {
value *= decay
if value < d.DecayToZero {
return 0
}
return value
}
rec.PeerScores.ReqResp.ValidResponses = decay(rec.PeerScores.ReqResp.ValidResponses, d.ValidResponseDecay)
rec.PeerScores.ReqResp.ErrorResponses = decay(rec.PeerScores.ReqResp.ErrorResponses, d.ErrorResponseDecay)
rec.PeerScores.ReqResp.RejectedPayloads = decay(rec.PeerScores.ReqResp.RejectedPayloads, d.RejectedPayloadDecay)
}
type PeerScores struct { type PeerScores struct {
Gossip GossipScores `json:"gossip"` Gossip GossipScores `json:"gossip"`
ReqRespSync float64 `json:"reqRespSync"` ReqResp ReqRespScores `json:"reqResp"`
} }
// ScoreDatastore defines a type-safe API for getting and setting libp2p peer score information // ScoreDatastore defines a type-safe API for getting and setting libp2p peer score information
......
...@@ -45,6 +45,88 @@ func TestUpdateGossipScore(t *testing.T) { ...@@ -45,6 +45,88 @@ func TestUpdateGossipScore(t *testing.T) {
assertPeerScores(t, store, id, PeerScores{Gossip: GossipScores{Total: score}}) assertPeerScores(t, store, id, PeerScores{Gossip: GossipScores{Total: score}})
} }
func TestIncrementValidResponses(t *testing.T) {
id := peer.ID("aaaa")
store := createMemoryStore(t)
inc := IncrementValidResponses{Cap: 2.1}
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{ValidResponses: 1}})
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{ValidResponses: 2}})
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{ValidResponses: 2.1}})
}
func TestIncrementErrorResponses(t *testing.T) {
id := peer.ID("aaaa")
store := createMemoryStore(t)
inc := IncrementErrorResponses{Cap: 2.1}
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{ErrorResponses: 1}})
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{ErrorResponses: 2}})
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{ErrorResponses: 2.1}})
}
func TestIncrementRejectedPayloads(t *testing.T) {
id := peer.ID("aaaa")
store := createMemoryStore(t)
inc := IncrementRejectedPayloads{Cap: 2.1}
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{RejectedPayloads: 1}})
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{RejectedPayloads: 2}})
setScoreRequired(t, store, id, inc)
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{RejectedPayloads: 2.1}})
}
func TestDecayApplicationScores(t *testing.T) {
id := peer.ID("aaaa")
store := createMemoryStore(t)
for i := 0; i < 10; i++ {
setScoreRequired(t, store, id, IncrementValidResponses{Cap: 100})
setScoreRequired(t, store, id, IncrementErrorResponses{Cap: 100})
setScoreRequired(t, store, id, IncrementRejectedPayloads{Cap: 100})
}
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{
ValidResponses: 10,
ErrorResponses: 10,
RejectedPayloads: 10,
}})
setScoreRequired(t, store, id, &DecayApplicationScores{
ValidResponseDecay: 0.8,
ErrorResponseDecay: 0.4,
RejectedPayloadDecay: 0.5,
DecayToZero: 0.1,
})
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{
ValidResponses: 10 * 0.8,
ErrorResponses: 10 * 0.4,
RejectedPayloads: 10 * 0.5,
}})
// Should be set to exactly zero when below DecayToZero
setScoreRequired(t, store, id, &DecayApplicationScores{
ValidResponseDecay: 0.8,
ErrorResponseDecay: 0.4,
RejectedPayloadDecay: 0.5,
DecayToZero: 5,
})
assertPeerScores(t, store, id, PeerScores{ReqResp: ReqRespScores{
ValidResponses: 10 * 0.8 * 0.8, // Not yet below 5 so preserved
ErrorResponses: 0,
RejectedPayloads: 0,
}})
}
func TestStoreScoresForMultiplePeers(t *testing.T) { func TestStoreScoresForMultiplePeers(t *testing.T) {
id1 := peer.ID("aaaa") id1 := peer.ID("aaaa")
id2 := peer.ID("bbbb") id2 := peer.ID("bbbb")
...@@ -215,7 +297,7 @@ func createPeerstoreWithBacking(t *testing.T, store *sync.MutexDatastore) Extend ...@@ -215,7 +297,7 @@ func createPeerstoreWithBacking(t *testing.T, store *sync.MutexDatastore) Extend
return eps return eps
} }
func setScoreRequired(t *testing.T, store ScoreDatastore, id peer.ID, diff *GossipScores) { func setScoreRequired(t *testing.T, store ScoreDatastore, id peer.ID, diff ScoreDiff) {
_, err := store.SetScore(id, diff) _, err := store.SetScore(id, diff)
require.NoError(t, err) require.NoError(t, err)
} }
...@@ -45,7 +45,31 @@ func TestParseHistoricSerializationsV0(t *testing.T) { ...@@ -45,7 +45,31 @@ func TestParseHistoricSerializationsV0(t *testing.T) {
IPColocationFactor: 12.34, IPColocationFactor: 12.34,
BehavioralPenalty: 56.78, BehavioralPenalty: 56.78,
}, },
ReqRespSync: 123456, ReqResp: ReqRespScores{},
},
LastUpdate: 1923841,
},
},
{
data: `{"peerScores":{"gossip":{"total":1234.52382,"blocks":{"timeInMesh":1234,"firstMessageDeliveries":12,"meshMessageDeliveries":34,"invalidMessageDeliveries":56},"IPColocationFactor":12.34,"behavioralPenalty":56.78},"reqResp":{"validResponses":99,"errorResponses":88,"rejectedPayloads":77}},"lastUpdate":1923841}`,
expected: scoreRecord{
PeerScores: PeerScores{
Gossip: GossipScores{
Total: 1234.52382,
Blocks: TopicScores{
TimeInMesh: 1234,
FirstMessageDeliveries: 12,
MeshMessageDeliveries: 34,
InvalidMessageDeliveries: 56,
},
IPColocationFactor: 12.34,
BehavioralPenalty: 56.78,
},
ReqResp: ReqRespScores{
ValidResponses: 99,
ErrorResponses: 88,
RejectedPayloads: 77,
},
}, },
LastUpdate: 1923841, LastUpdate: 1923841,
}, },
......
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