Skip to content

Commit

Permalink
CNS-1000: change pairing score to use LegacyDec (#1599)
Browse files Browse the repository at this point in the history
  • Loading branch information
oren-lava authored Dec 16, 2024
1 parent 0c91449 commit a5590f6
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 50 deletions.
13 changes: 13 additions & 0 deletions periods.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"start_time": 1733431162,
"periods":[
{
"coins": "10ulava",
"length_seconds":2592000
},
{
"coins": "10ulava",
"length_seconds":2592000
}
]
}
2 changes: 1 addition & 1 deletion protocol/lavasession/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func GetTlsConfig(networkAddress NetworkAddressData) *tls.Config {
}

func SortByGeolocations(pairingEndpoints []*Endpoint, currentGeo planstypes.Geolocation) {
latencyToGeo := func(a, b planstypes.Geolocation) uint64 {
latencyToGeo := func(a, b planstypes.Geolocation) int64 {
_, latency := scores.CalcGeoLatency(a, []planstypes.Geolocation{b})
return latency
}
Expand Down
4 changes: 2 additions & 2 deletions x/pairing/keeper/pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ func (k Keeper) GetPairingForClient(ctx sdk.Context, chainID string, clientAddre

// CalculatePairingChance calculates the chance of a provider to be picked in the pairing process for the first pairing slot
func (k Keeper) CalculatePairingChance(ctx sdk.Context, provider string, chainID string, policy *planstypes.Policy, cluster string) (cosmosmath.LegacyDec, error) {
totalScore := cosmosmath.ZeroUint()
providerScore := cosmosmath.ZeroUint()
totalScore := cosmosmath.LegacyZeroDec()
providerScore := cosmosmath.LegacyZeroDec()

_, _, scores, err := k.getPairingForClient(ctx, chainID, uint64(ctx.BlockHeight()), policy, cluster, "dummy", true)
if err != nil {
Expand Down
21 changes: 10 additions & 11 deletions x/pairing/keeper/scores/geo_req.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/lavanet/lava/v4/utils"
planstypes "github.com/lavanet/lava/v4/x/plans/types"
)
Expand All @@ -15,9 +14,9 @@ type GeoReq struct {
}

const (
geoReqName = "geo-req"
maxGeoLatency uint64 = 10000 // highest geo cost < 300
minGeoLatency = 1
geoReqName = "geo-req"
maxGeoLatency int64 = 10000 // highest geo cost < 300
minGeoLatency = 1
)

func (gr GeoReq) Init(policy planstypes.Policy) bool {
Expand All @@ -26,7 +25,7 @@ func (gr GeoReq) Init(policy planstypes.Policy) bool {

// Score calculates the geo score of a provider based on preset latency data
// Note: each GeoReq must have exactly a single geolocation (bit)
func (gr GeoReq) Score(score PairingScore) math.Uint {
func (gr GeoReq) Score(score PairingScore) math.LegacyDec {
// check if the provider supports the required geolocation
if gr.Geo&^score.Provider.Geolocation == 0 {
return calculateCostFromLatency(minGeoLatency)
Expand Down Expand Up @@ -68,13 +67,13 @@ func (gr GeoReq) GetReqForSlot(policy planstypes.Policy, slotIdx int) ScoreReq {
}

// CalcGeoCost() finds the minimal latency between the required geo and the provider's supported geolocations
func CalcGeoCost(reqGeo planstypes.Geolocation, providerGeos []planstypes.Geolocation) (minLatencyGeo planstypes.Geolocation, minLatencyCost math.Uint) {
func CalcGeoCost(reqGeo planstypes.Geolocation, providerGeos []planstypes.Geolocation) (minLatencyGeo planstypes.Geolocation, minLatencyCost math.LegacyDec) {
minGeo, minLatency := CalcGeoLatency(reqGeo, providerGeos)

return minGeo, calculateCostFromLatency(minLatency)
}

func CalcGeoLatency(reqGeo planstypes.Geolocation, providerGeos []planstypes.Geolocation) (planstypes.Geolocation, uint64) {
func CalcGeoLatency(reqGeo planstypes.Geolocation, providerGeos []planstypes.Geolocation) (planstypes.Geolocation, int64) {
minGeo := planstypes.Geolocation(-1)
minLatency := maxGeoLatency
for _, pGeo := range providerGeos {
Expand All @@ -95,19 +94,19 @@ func CalcGeoLatency(reqGeo planstypes.Geolocation, providerGeos []planstypes.Geo
return minGeo, minLatency
}

func calculateCostFromLatency(latency uint64) math.Uint {
func calculateCostFromLatency(latency int64) math.LegacyDec {
if latency == 0 {
utils.LavaFormatWarning("got latency 0 when calculating geo req score", fmt.Errorf("invalid geo req score"))
return math.OneUint()
return math.LegacyOneDec()
}
return sdk.NewUint(maxGeoLatency / latency)
return math.LegacyNewDec(maxGeoLatency).QuoInt64(latency)
}

// GEO_LATENCY_MAP is a map of lists of GeoLatency that defines the cost of geo mismatch
// for each single geolocation. The map key is a single geolocation and the value is an
// ordered list of neighbors and their latency (ordered by latency)
// latency data from: https://wondernetwork.com/pings (July 2023)
var GEO_LATENCY_MAP = map[planstypes.Geolocation]map[planstypes.Geolocation]uint64{
var GEO_LATENCY_MAP = map[planstypes.Geolocation]map[planstypes.Geolocation]int64{
planstypes.Geolocation_AS: {
planstypes.Geolocation_AU: 146,
planstypes.Geolocation_EU: 155,
Expand Down
4 changes: 2 additions & 2 deletions x/pairing/keeper/scores/geo_req_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestCalcGeoCost(t *testing.T) {
reqGeo planstypes.Geolocation
providerGeos []planstypes.Geolocation
expectedGeo planstypes.Geolocation
expectedCostUint uint64
expectedCostUint int64
}{
{
name: "happy flow",
Expand Down Expand Up @@ -59,7 +59,7 @@ func TestGeoReqScore(t *testing.T) {
name string
reqGeo int32
providerGeo int32
expectedLatency uint64
expectedLatency int64
}{
{
name: "happy flow - provider supports geo",
Expand Down
8 changes: 4 additions & 4 deletions x/pairing/keeper/scores/pairing_score.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const (
// PairingScore holds a provider's score with respect to a set of requirements (ScoreReq), indexed by their unique name.
type PairingScore struct {
Provider *epochstoragetypes.StakeEntry
Score math.Uint
ScoreComponents map[string]math.Uint
Score math.LegacyDec
ScoreComponents map[string]math.LegacyDec
SkipForSelection bool
SlotFiltering map[int]struct{} // slot indexes here are skipped
QosExcellenceReport pairingtypes.QualityOfServiceReport
Expand Down Expand Up @@ -43,8 +43,8 @@ func (ps *PairingScore) InvalidIndexes(possibleIndexes []int) []int {
func NewPairingScore(stakeEntry *epochstoragetypes.StakeEntry, qos pairingtypes.QualityOfServiceReport) *PairingScore {
score := PairingScore{
Provider: stakeEntry,
Score: math.OneUint(),
ScoreComponents: map[string]math.Uint{},
Score: math.LegacyOneDec(),
ScoreComponents: map[string]math.LegacyDec{},
SkipForSelection: false,
QosExcellenceReport: qos,
}
Expand Down
4 changes: 2 additions & 2 deletions x/pairing/keeper/scores/qos_req.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (qr QosReq) Init(policy planstypes.Policy) bool {
}

// Score calculates the the provider's qos score
func (qr QosReq) Score(score PairingScore) math.Uint {
func (qr QosReq) Score(score PairingScore) math.LegacyDec {
// TODO: update Qos in providerQosFS properly and uncomment this code below
// Also, the qos score should range between 0.5-2

Expand All @@ -31,7 +31,7 @@ func (qr QosReq) Score(score PairingScore) math.Uint {
// }

// return math.Uint(qosScore)
return math.NewUint(1)
return math.LegacyOneDec()
}

func (qr QosReq) GetName() string {
Expand Down
32 changes: 15 additions & 17 deletions x/pairing/keeper/scores/score.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,22 +148,20 @@ func CalcPairingScore(scores []*PairingScore, strategy ScoreStrategy, diffSlot P
}

newScoreComp := req.Score(*score)
if newScoreComp == math.ZeroUint() {
if newScoreComp == math.LegacyZeroDec() {
return utils.LavaFormatError("new score component is zero", fmt.Errorf("cannot calculate pairing score"),
utils.Attribute{Key: "score component", Value: reqName},
utils.Attribute{Key: "provider", Value: score.Provider.Address},
)
}
newScoreCompDec := sdk.NewDecFromInt(math.Int(newScoreComp))
newScoreCompDec = newScoreCompDec.Power(weight)
newScoreComp = math.Uint(newScoreCompDec.TruncateInt())
newScoreComp = newScoreComp.Power(weight)

// update the score component map
score.ScoreComponents[reqName] = newScoreComp
}

// calc new score
newScore := math.OneUint()
newScore := math.LegacyOneDec()
for _, scoreComp := range score.ScoreComponents {
newScore = newScore.Mul(scoreComp)
}
Expand Down Expand Up @@ -200,8 +198,8 @@ func PickProviders(ctx sdk.Context, scores []*PairingScore, groupIndexes []int,
groupIndex = -1
effectiveScore = totalScore
}
randomValue := uint64(rng.Int63n(effectiveScore.BigInt().Int64())) + 1
newScoreSum := math.ZeroUint()
randomValue := rng.Int63n(effectiveScore.RoundInt64()) + 1
newScoreSum := math.LegacyZeroDec()

for idx := len(scores) - 1; idx >= 0; idx-- {
if !scores[idx].IsValidForSelection(groupIndex) {
Expand All @@ -210,7 +208,7 @@ func PickProviders(ctx sdk.Context, scores []*PairingScore, groupIndexes []int,
}
providerScore := scores[idx]
newScoreSum = newScoreSum.Add(providerScore.Score)
if randomValue <= newScoreSum.Uint64() {
if randomValue <= newScoreSum.RoundInt64() {
// we hit our chosen provider
// remove this provider from the random pool, so the sum is lower now

Expand All @@ -228,27 +226,27 @@ func PickProviders(ctx sdk.Context, scores []*PairingScore, groupIndexes []int,
// the negative score modifiers contain the total stake of providers that are not allowed
// so if a provider is not allowed in slot X, it will be added to the total score but will have slotIndexScore[X]+= providerStake
// and during the selection of slot X we will have the selection effective sum as: totalScore - slotIndexScore[X], and that provider that is not allowed can't be selected
func CalculateTotalScoresForGroup(scores []*PairingScore, groupIndexes []int) (totalScore math.Uint, slotIndexScore map[int]math.Uint, err error) {
func CalculateTotalScoresForGroup(scores []*PairingScore, groupIndexes []int) (totalScore math.LegacyDec, slotIndexScore map[int]math.LegacyDec, err error) {
if len(scores) == 0 {
return math.ZeroUint(), nil, fmt.Errorf("invalid scores length")
return math.LegacyZeroDec(), nil, fmt.Errorf("invalid scores length")
}
totalScore = math.ZeroUint()
slotIndexScore = map[int]math.Uint{}
totalScore = math.LegacyZeroDec()
slotIndexScore = map[int]math.LegacyDec{}
for _, groupIndex := range groupIndexes {
slotIndexScore[groupIndex] = math.ZeroUint()
slotIndexScore[groupIndex] = math.LegacyZeroDec()
}
// all all providers to selection possibilities
for _, providerScore := range scores {
totalScore, slotIndexScore = AddProviderToSelection(providerScore, groupIndexes, totalScore, slotIndexScore)
}

if totalScore == math.ZeroUint() {
return math.ZeroUint(), nil, utils.LavaFormatError("score sum is zero", fmt.Errorf("cannot pick providers for pairing"))
if totalScore == math.LegacyZeroDec() {
return math.LegacyZeroDec(), nil, utils.LavaFormatError("score sum is zero", fmt.Errorf("cannot pick providers for pairing"))
}
return totalScore, slotIndexScore, nil
}

func AddProviderToSelection(providerScore *PairingScore, groupIndexes []int, totalScore math.Uint, slotIndexScore map[int]math.Uint) (math.Uint, map[int]math.Uint) {
func AddProviderToSelection(providerScore *PairingScore, groupIndexes []int, totalScore math.LegacyDec, slotIndexScore map[int]math.LegacyDec) (math.LegacyDec, map[int]math.LegacyDec) {
if providerScore.SkipForSelection {
return totalScore, slotIndexScore
}
Expand All @@ -260,7 +258,7 @@ func AddProviderToSelection(providerScore *PairingScore, groupIndexes []int, tot
return totalScore, slotIndexScore
}

func RemoveProviderFromSelection(providerScore *PairingScore, groupIndexes []int, totalScore math.Uint, slotIndexScore map[int]math.Uint) (math.Uint, map[int]math.Uint) {
func RemoveProviderFromSelection(providerScore *PairingScore, groupIndexes []int, totalScore math.LegacyDec, slotIndexScore map[int]math.LegacyDec) (math.LegacyDec, map[int]math.LegacyDec) {
// remove this provider from the total score
totalScore = totalScore.Sub(providerScore.Score)
// remove this provider for the subtraction scores as well
Expand Down
2 changes: 1 addition & 1 deletion x/pairing/keeper/scores/score_req.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type ScoreReq interface {
// Init() initializes the ScoreReq object and returns whether it's active
Init(policy planstypes.Policy) bool
// Score() calculates a provider's score according to the requirement
Score(score PairingScore) math.Uint
Score(score PairingScore) math.LegacyDec
// GetName returns the unique name of the ScoreReq implementation
GetName() string
// Equal compares two ScoreReq objects
Expand Down
10 changes: 5 additions & 5 deletions x/pairing/keeper/scores/score_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestTotalScore(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
totalScore, sloitIndexScores, err := CalculateTotalScoresForGroup(tt.scores, tt.groupIndexes)
require.NoError(t, err)
calculatedScore := math.ZeroUint()
calculatedScore := math.LegacyZeroDec()
for _, slotIndexScore := range sloitIndexScores {
calculatedScore = calculatedScore.Add(slotIndexScore)
}
Expand All @@ -59,8 +59,8 @@ func generateScores(count int, slotFilterIndex int) []*PairingScore {
for i := 0; i < count; i++ {
pairingScore := &PairingScore{
Provider: nil,
Score: math.ZeroUint().AddUint64(100),
ScoreComponents: map[string]math.Uint{},
Score: math.LegacyNewDec(100),
ScoreComponents: map[string]math.LegacyDec{},
}
if slotFilterIndex >= 0 {
pairingScore.SlotFiltering = map[int]struct{}{slotFilterIndex: {}}
Expand All @@ -75,8 +75,8 @@ func generateScoresWithRandomFilter(count int) []*PairingScore {
for i := 0; i < count; i++ {
pairingScore := &PairingScore{
Provider: nil,
Score: math.ZeroUint().AddUint64(100),
ScoreComponents: map[string]math.Uint{},
Score: math.LegacyNewDec(100),
ScoreComponents: map[string]math.LegacyDec{},
}
pairingScore.SlotFiltering = map[int]struct{}{rand.Int(): {}}
ret = append(ret, pairingScore)
Expand Down
9 changes: 4 additions & 5 deletions x/pairing/keeper/scores/stake_req.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package scores

import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
planstypes "github.com/lavanet/lava/v4/x/plans/types"
)

Expand All @@ -16,15 +15,15 @@ func (sr *StakeReq) Init(policy planstypes.Policy) bool {
}

// Score calculates the the provider score as the normalized stake
func (sr *StakeReq) Score(score PairingScore) math.Uint {
func (sr *StakeReq) Score(score PairingScore) math.LegacyDec {
if sr == nil {
return math.OneUint()
return math.LegacyOneDec()
}
effectiveStake := score.Provider.TotalStake()
if !effectiveStake.IsPositive() {
return math.OneUint()
return math.LegacyOneDec()
}
return sdk.NewUint(effectiveStake.Uint64())
return math.LegacyNewDecFromInt(effectiveStake)
}

func (sr *StakeReq) GetName() string {
Expand Down

0 comments on commit a5590f6

Please sign in to comment.