Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Reputation: CNS-1004 - QoS excellence epoch score aggregation #1612

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
54a0136
CNS-1004: implement qos excellence score aggregation from relay payments
oren-lava Aug 5, 2024
e3f0183
CNS-1004: unit tests
oren-lava Aug 5, 2024
4471779
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Aug 5, 2024
44f6f9b
CNS-1005: implement decay factor
oren-lava Aug 6, 2024
d1a4117
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Aug 6, 2024
e655ba7
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Aug 6, 2024
883c25c
CNS-1005: update reputation on epoch start and set repuation pairing …
oren-lava Aug 6, 2024
841e0e8
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Aug 6, 2024
32a544c
CNS-1004: add stake to reputation
oren-lava Aug 6, 2024
f478f6c
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Aug 6, 2024
820f179
CNS-1005: fix error comments
oren-lava Aug 6, 2024
1a599a0
CNS-1004: add stake check in unit test
oren-lava Aug 7, 2024
350b1d9
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Aug 7, 2024
82faf7f
CNS-1005: add support for reputation pairing req (currently disabled)
oren-lava Aug 7, 2024
f4eb857
CNS-1005: remove redundant func
oren-lava Aug 7, 2024
5e56157
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Aug 7, 2024
949e80f
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Aug 7, 2024
2ff09f5
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Aug 8, 2024
539f403
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Aug 8, 2024
36c47b3
CNS-1005: small fixes
oren-lava Aug 11, 2024
ce24252
CNS-1005: make higher pairing score for lower QoS score and make func…
oren-lava Aug 12, 2024
7bf01ba
CNS-1005: partial unit tests
oren-lava Aug 12, 2024
1215c13
CNS-1005: unit tests
oren-lava Aug 12, 2024
994e762
CNS-1004: minor adds
oren-lava Aug 13, 2024
f87d8b7
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Aug 13, 2024
a160860
CNS-1006: impelemnted queries and partial unit test
oren-lava Aug 15, 2024
37cd180
CNS-1006: small adjustments and README update
oren-lava Aug 15, 2024
d452aff
CNS-1006: small fix and added unit test
oren-lava Aug 15, 2024
13deb57
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Aug 15, 2024
d115877
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Aug 15, 2024
fc75fd0
Merge branch 'CNS-1005-reputation-pairing-score' into CNS-1006-reputa…
oren-lava Aug 15, 2024
31f5ddd
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Aug 29, 2024
8da9b2f
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Aug 29, 2024
489fb2d
Merge branch 'CNS-1005-reputation-pairing-score' into CNS-1006-reputa…
oren-lava Aug 29, 2024
21e2819
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Sep 4, 2024
8a34d74
CNS-1004: fix after merge
oren-lava Sep 4, 2024
f540b6e
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Sep 4, 2024
a6d45f2
Merge branch 'CNS-1005-reputation-pairing-score' into CNS-1006-reputa…
oren-lava Sep 4, 2024
b7c747f
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Dec 5, 2024
e374c8b
small fixes
oren-lava Dec 5, 2024
e5df7d1
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Dec 5, 2024
b47f2e0
Merge branch 'CNS-1005-reputation-pairing-score' into CNS-1006-reputa…
oren-lava Dec 5, 2024
c7e2a57
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Jan 5, 2025
5ad9aae
pr fix
oren-lava Jan 5, 2025
1828192
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Jan 5, 2025
0b0e719
remove panics from reputation update failure
oren-lava Jan 5, 2025
094ee32
various commnets and small fixes
oren-lava Jan 5, 2025
0989d8d
avoid string parsing
oren-lava Jan 5, 2025
f160ec7
fix bug in which TimeLastUpdated was updated every relay payment and …
oren-lava Jan 6, 2025
ee2cf55
upon update calculate variance first and then the score
oren-lava Jan 6, 2025
0cd75a5
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Jan 6, 2025
e86b10c
fix unit test
oren-lava Jan 6, 2025
2ffb2e1
Merge branch 'CNS-1005-reputation-pairing-score' into CNS-1006-reputa…
oren-lava Jan 6, 2025
2262838
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Jan 7, 2025
600464d
pr fixes
oren-lava Jan 9, 2025
3f0248d
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Jan 9, 2025
5073c38
small PR fixes
oren-lava Jan 9, 2025
2303348
change half life param to be uint64
oren-lava Jan 9, 2025
eb405f0
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Jan 12, 2025
5fe5fac
Merge branch 'CNS-1004-reputation-qos-score-aggregation' into CNS-100…
oren-lava Jan 12, 2025
87fb340
delete pairing query cache
oren-lava Jan 12, 2025
b919b1e
calculate exp without floats
oren-lava Jan 12, 2025
97b42c1
Merge branch 'CNS-1005-reputation-pairing-score' into CNS-1006-reputa…
oren-lava Jan 12, 2025
859642d
change query arg name
oren-lava Jan 12, 2025
c2512e2
Merge pull request #1631 from lavanet/CNS-1006-reputation-queries
Yaroms Jan 12, 2025
e1fb93d
Merge pull request #1626 from lavanet/CNS-1005-reputation-pairing-score
Yaroms Jan 12, 2025
062ae0d
weigh epoch qos score by cu (revert by relay num)
oren-lava Jan 13, 2025
23ee142
Merge branch 'CNS-1003-reputation-proto-definitions' into CNS-1004-re…
oren-lava Jan 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion proto/lavanet/lava/pairing/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ message Params {
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
int64 reputation_half_life_factor = 17 [(gogoproto.moretags) = "yaml:\"reputation_half_life_factor\""];
uint64 reputation_half_life_factor = 17 [(gogoproto.moretags) = "yaml:\"reputation_half_life_factor\""];
uint64 reputation_relay_failure_cost = 18 [(gogoproto.moretags) = "yaml:\"reputation_relay_failure_cost\""];
}
52 changes: 49 additions & 3 deletions proto/lavanet/lava/pairing/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "lavanet/lava/pairing/params.proto";
import "lavanet/lava/pairing/reputation.proto";
import "lavanet/lava/spec/spec.proto";


Expand Down Expand Up @@ -80,9 +81,19 @@ service Query {
}

// Queries a for the aggregated CU of all ProviderEpochCu objects all the providers.
rpc ProvidersEpochCu(QueryProvidersEpochCuRequest) returns (QueryProvidersEpochCuResponse) {
option (google.api.http).get = "/lavanet/lava/pairing/providers_epoch_cu";
}
rpc ProvidersEpochCu(QueryProvidersEpochCuRequest) returns (QueryProvidersEpochCuResponse) {
option (google.api.http).get = "/lavanet/lava/pairing/providers_epoch_cu";
}

// Queries a for a provider reputation.
rpc ProviderReputation(QueryProviderReputationRequest) returns (QueryProviderReputationResponse) {
option (google.api.http).get = "/lavanet/lava/pairing/provider_reputation/{provider}/{chainID}/{cluster}";
}

// Queries a for a provider reputation's details (mainly for developers).
rpc ProviderReputationDetails(QueryProviderReputationDetailsRequest) returns (QueryProviderReputationDetailsResponse) {
option (google.api.http).get = "/lavanet/lava/pairing/provider_reputation_details/{address}/{chainID}/{cluster}";
}

// this line is used by starport scaffolding # 2

Expand Down Expand Up @@ -238,4 +249,39 @@ message QueryProvidersEpochCuResponse {
message ProviderCuInfo {
string provider = 1;
uint64 cu = 2;
}

message QueryProviderReputationRequest {
string provider = 1;
string chainID = 2;
string cluster = 3;
}

message ReputationData {
uint64 rank = 1; // rank compared to other providers
uint64 providers = 2; // amount of providers with the same chainID+cluster
string overall_performance = 3; // overall performance metric which can be "good", "bad", or "low variance"
string chainID = 4;
string cluster = 5;
}

message QueryProviderReputationResponse {
repeated ReputationData data = 1 [(gogoproto.nullable) = false];
}

message QueryProviderReputationDetailsRequest {
string address = 1;
string chainID = 2;
string cluster = 3;
}

message ReputationDevData {
Reputation reputation = 1 [(gogoproto.nullable) = false];
ReputationPairingScore reputation_pairing_score = 2 [(gogoproto.nullable) = false];
string chainID = 4;
string cluster = 5;
}

message QueryProviderReputationDetailsResponse {
repeated ReputationDevData data = 1 [(gogoproto.nullable) = false];
}
7 changes: 7 additions & 0 deletions proto/lavanet/lava/pairing/relay.proto
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,23 @@ message RelayReply {
}

message QualityOfServiceReport{
// Latency of provider answers in milliseconds, range 0-inf, lower is better
string latency = 1 [
(gogoproto.moretags) = "yaml:\"Latency\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];

// Percentage of times the provider returned a non-error response, range 0-1, higher is better
string availability = 2 [
(gogoproto.moretags) = "yaml:\"availability\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];

// Amount of time the provider is not synced (have the latest block) in milliseconds, range 0-inf, lower is better.
// Example: in ETH we have 15sec block time. So sync = 15000 means that the provider is one block
// behind the actual latest block.
string sync = 3 [
(gogoproto.moretags) = "yaml:\"sync\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
Expand Down
2 changes: 2 additions & 0 deletions scripts/test/cli_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ trace lavad q pairing static-providers-list LAV1 >/dev/null
trace lavad q pairing user-entry $(lavad keys show alice -a) ETH1 20 >/dev/null
trace lavad q pairing verify-pairing STRK $(lavad keys show alice -a) $(lavad keys show alice -a) 60 >/dev/null
trace lavad q pairing provider-pairing-chance $(lavad keys show servicer1 -a) STRK 1 "" >/dev/null
trace lavad q pairing provider-reputation $(lavad keys show servicer1 -a) ETH1 free >/dev/null
trace lavad q pairing provider-reputation-details $(lavad keys show servicer1 -a) ETH1 free >/dev/null

echo "Testing dualstaking tx commands"
wait_count_blocks 1 >/dev/null
Expand Down
29 changes: 29 additions & 0 deletions testutil/common/tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,26 @@ func (ts *Tester) QueryPairingProviderEpochCu(provider string, project string, c
return ts.Keepers.Pairing.ProvidersEpochCu(ts.GoCtx, msg)
}

// QueryPairingProviderReputation implements 'q pairing provider-reputation'
func (ts *Tester) QueryPairingProviderReputation(provider string, chainID string, cluster string) (*pairingtypes.QueryProviderReputationResponse, error) {
msg := &pairingtypes.QueryProviderReputationRequest{
Provider: provider,
ChainID: chainID,
Cluster: cluster,
}
return ts.Keepers.Pairing.ProviderReputation(ts.GoCtx, msg)
}

// QueryPairingProviderReputationDetails implements 'q pairing provider-reputation-details'
func (ts *Tester) QueryPairingProviderReputationDetails(provider string, chainID string, cluster string) (*pairingtypes.QueryProviderReputationDetailsResponse, error) {
msg := &pairingtypes.QueryProviderReputationDetailsRequest{
Address: provider,
ChainID: chainID,
Cluster: cluster,
}
return ts.Keepers.Pairing.ProviderReputationDetails(ts.GoCtx, msg)
}

// QueryPairingSubscriptionMonthlyPayout implements 'q pairing subscription-monthly-payout'
func (ts *Tester) QueryPairingSubscriptionMonthlyPayout(consumer string) (*pairingtypes.QuerySubscriptionMonthlyPayoutResponse, error) {
msg := &pairingtypes.QuerySubscriptionMonthlyPayoutRequest{
Expand Down Expand Up @@ -1091,6 +1111,15 @@ func (ts *Tester) GetNextMonth(from time.Time) int64 {
return utils.NextMonth(from).UTC().Unix()
}

func (ts *Tester) BlockTimeDefault() time.Duration {
return ts.Keepers.Downtime.GetParams(ts.Ctx).DowntimeDuration
}

func (ts *Tester) EpochTimeDefault() time.Duration {
epochBlocks := ts.Keepers.Epochstorage.GetParams(ts.Ctx).EpochBlocks
return ts.BlockTimeDefault() * time.Duration(epochBlocks)
}

func (ts *Tester) AdvanceToBlock(block uint64) {
if block < ts.BlockHeight() {
panic("AdvanceToBlock: block in the past: " +
Expand Down
8 changes: 8 additions & 0 deletions utils/convert.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package utils

import "math"

func SafeUint64ToInt64Convert(val uint64) int64 {
if val > math.MaxInt64 {
val = math.MaxInt64
}
return int64(val)
}
func Btof(b bool) float64 {
if b {
return 1
Expand Down
40 changes: 39 additions & 1 deletion utils/math.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package utils

import "golang.org/x/exp/constraints"
import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"golang.org/x/exp/constraints"
)

const (
DecayFactorNaturalBaseString = "2.71828182845904523536028747135266249775724709369995957496696763"
)

func Min[T constraints.Ordered](x, y T) T {
if x < y {
Expand All @@ -15,3 +23,33 @@ func Max[T constraints.Ordered](x, y T) T {
}
return y
}

// NaturalBaseExponentFraction calculates an exponent of the
// natural base e using a sdk.Dec. using the formula: e^(numerator / denominator)
// since it is not possible to directly calculate a power of a fraction,
// we're doing it in three steps:
// 1. Calculate e^numerator
// 2. Take the denominatorth root
// 3. Take the reciprocal (if negative=true)
func NaturalBaseExponentFraction(numerator, denominator int64, negative bool) math.LegacyDec {
numeratorUint64 := uint64(numerator)
denominatorUint64 := uint64(denominator)

e := sdk.MustNewDecFromStr(DecayFactorNaturalBaseString)

// Step 1: Calculate e^a
eToA := e.Power(numeratorUint64)

// Step 2: Take the bth root
result, err := eToA.ApproxRoot(denominatorUint64)
if err != nil {
panic(err)
}

if negative {
// Step 3: Take the reciprocal
result = sdk.OneDec().Quo(result)
}

return result
}
5 changes: 5 additions & 0 deletions x/pairing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,13 @@ The pairing module supports the following queries:
| `list-epoch-payments` | none | show all epochPayment objects |
| `list-provider-payment-storage` | none | show all providerPaymentStorage objects |
| `list-unique-payment-storage-client-provider` | none | show all uniquePaymentStorageClientProvider objects |
| `provider` | chain-id (string) | show a provider staked on a specific chain |
| `provider-monthly-payout` | provider (string) | show the current monthly payout for a specific provider |
| `provider-pairing-chance` | provider (string), chain-id (string) | show the chance of a provider has to be part of the pairing list for a specific chain |
| `provider-reputation` | provider (string), chain-id (string), cluster (string) | show the provider's rank compared to other provider with the same chain-id and cluster by their reputation score |
| `provider-reputation-details` | provider (string), chain-id (string), cluster (string) | developer query to show the provider's reputation score raw data |
| `providers` | chain-id (string) | show all the providers staked on a specific chain |
| `providers-epoch-cu` | | developer query to list the amount of CU serviced by all the providers every epoch |
| `sdk-pairing` | none | query used by Lava-SDK to get all the required pairing info |
| `show-epoch-payments` | index (string) | show an epochPayment object by index |
| `show-provider-payment-storage` | index (string) | show a providerPaymentStorage object by index |
Expand Down
2 changes: 2 additions & 0 deletions x/pairing/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func GetQueryCmd(queryRoute string) *cobra.Command {
cmd.AddCommand(CmdSubscriptionMonthlyPayout())

cmd.AddCommand(CmdProvidersEpochCu())
cmd.AddCommand(CmdProviderReputation())
cmd.AddCommand(CmdProviderReputationDetails())

cmd.AddCommand(CmdDebugQuery())

Expand Down
65 changes: 65 additions & 0 deletions x/pairing/client/cli/query_provider_reputation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package cli

import (
"strconv"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/lavanet/lava/v4/utils"
"github.com/lavanet/lava/v4/x/pairing/types"
"github.com/spf13/cobra"
)

var _ = strconv.Itoa(0)

func CmdProviderReputation() *cobra.Command {
cmd := &cobra.Command{
Use: "provider-reputation [address] [chain-id] [cluster]",
Short: "Query for a provider's reputation. Use \"*\" for specify all for chain/cluster.",
Args: cobra.ExactArgs(3),
Example: `
Reputation of alice for chain ETH1 and the cluster "free":
lavad q pairing provider-reputation alice ETH1 free

Reputation of alice for all chains and the cluster "free":
lavad q pairing provider-reputation alice * free

Reputation of alice for ETH1 and for all clusters:
lavad q pairing provider-reputation alice ETH1 *

Reputation of alice for all chains and for all clusters:
lavad q pairing provider-reputation alice * *`,
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

address, err := utils.ParseCLIAddress(clientCtx, args[0])
if err != nil {
return err
}
chainID := args[1]
cluster := args[2]

queryClient := types.NewQueryClient(clientCtx)

params := &types.QueryProviderReputationRequest{
Provider: address,
ChainID: chainID,
Cluster: cluster,
}

res, err := queryClient.ProviderReputation(cmd.Context(), params)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
65 changes: 65 additions & 0 deletions x/pairing/client/cli/query_provider_reputation_details.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package cli

import (
"strconv"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/lavanet/lava/v4/utils"
"github.com/lavanet/lava/v4/x/pairing/types"
"github.com/spf13/cobra"
)

var _ = strconv.Itoa(0)

func CmdProviderReputationDetails() *cobra.Command {
cmd := &cobra.Command{
Use: "provider-reputation-details [address] [chain-id] [cluster]",
Short: "Query for a provider's reputation details. Mainly used by developers. Use \"*\" for specify all for chain/cluster.",
Args: cobra.ExactArgs(3),
Example: `
Reputation details of alice for chain ETH1 and the cluster "free":
lavad q pairing provider-reputation-details alice ETH1 free

Reputation details of alice for all chains and the cluster "free":
lavad q pairing provider-reputation-details alice * free

Reputation details of alice for ETH1 and for all clusters:
lavad q pairing provider-reputation-details alice ETH1 *

Reputation details of alice for all chains and for all clusters:
lavad q pairing provider-reputation-details alice * *`,
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

address, err := utils.ParseCLIAddress(clientCtx, args[0])
if err != nil {
return err
}
chainID := args[1]
cluster := args[2]

queryClient := types.NewQueryClient(clientCtx)

params := &types.QueryProviderReputationDetailsRequest{
Address: address,
ChainID: chainID,
Cluster: cluster,
}

res, err := queryClient.ProviderReputationDetails(cmd.Context(), params)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
4 changes: 2 additions & 2 deletions x/pairing/client/cli/query_providers_epoch_cu.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
func CmdProvidersEpochCu() *cobra.Command {
cmd := &cobra.Command{
Use: "providers-epoch-cu",
Short: "Query to show the amount of CU serviced by all provider in a specific epoch",
Short: "Query to list the amount of CU serviced by all the providers every epoch",
Example: `
lavad q pairing providers-epoch-cu`,
Args: cobra.ExactArgs(1),
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
Expand Down
Loading
Loading