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-1003: reputation proto definitions #1604

Open
wants to merge 19 commits into
base: CNS-1002-collect-qos-raw-data
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6302237
CNS-1003: install collections
oren-lava Jul 31, 2024
40f8187
CNS-1003: implemented reputation
oren-lava Aug 1, 2024
9535548
CNS-1003: rename providerQosFs to reputationsFS
oren-lava Aug 1, 2024
47a5a69
CNS-1003: add reputation pairing score functions
oren-lava Aug 1, 2024
c83351d
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Aug 4, 2024
68b1c3f
CNS-1003: minor changes
oren-lava Aug 4, 2024
c047f2d
CNS-1003: add small comment
oren-lava Aug 4, 2024
0ceea02
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Aug 4, 2024
9d37f1d
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Aug 4, 2024
74bcc55
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Aug 4, 2024
d381d7b
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Aug 5, 2024
8613a69
CNS-1003: revert change in protoc_grpc_relay.sh
oren-lava Aug 5, 2024
e79b17c
CNS-1003: add stake to reputation and remove ref indices
oren-lava Aug 6, 2024
cd3db87
CNS-1003: minor changes
oren-lava Aug 6, 2024
351ce37
CNS-1003: make default QoS score zero
oren-lava Aug 7, 2024
48757ca
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Aug 8, 2024
37471dc
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Aug 15, 2024
eb40078
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Aug 29, 2024
468e86a
Merge branch 'CNS-1002-collect-qos-raw-data' into CNS-1003-reputation…
oren-lava Sep 4, 2024
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 app/upgrades/empty_upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func v0_23_0_UpgradeHandler(
lk *keepers.LavaKeepers,
) upgradetypes.UpgradeHandler {
return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
lk.PairingKeeper.InitProviderQoS(ctx, *fixationtypes.DefaultGenesis())
lk.PairingKeeper.InitReputations(ctx, *fixationtypes.DefaultGenesis())
return m.RunMigrations(ctx, c, vm)
}
}
Expand Down
15 changes: 11 additions & 4 deletions proto/lavanet/lava/pairing/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "lavanet/lava/pairing/params.proto";
import "lavanet/lava/pairing/epoch_cu.proto";
import "lavanet/lava/fixationstore/fixation.proto";
import "lavanet/lava/timerstore/timer.proto";
import "lavanet/lava/pairing/reputation.proto";

// this line is used by starport scaffolding # genesis/proto/import

Expand All @@ -18,17 +19,16 @@ message BadgeUsedCu {

// GenesisState defines the pairing module's genesis state.
message GenesisState {
reserved 2,3,4,7;
Params params = 1 [(gogoproto.nullable) = false];
reserved 2;
reserved 3;
reserved 4;
repeated BadgeUsedCu badgeUsedCuList = 5 [(gogoproto.nullable) = false];
lavanet.lava.timerstore.GenesisState badgesTS = 6 [(gogoproto.nullable) = false];
lavanet.lava.fixationstore.GenesisState providerQosFS = 7 [(gogoproto.nullable) = false];
repeated UniqueEpochSessionGenesis unique_epoch_sessions = 8 [(gogoproto.nullable) = false];
repeated ProviderEpochCuGenesis provider_epoch_cus = 9 [(gogoproto.nullable) = false];
repeated ProviderEpochComplainerCuGenesis provider_epoch_complained_cus = 10 [(gogoproto.nullable) = false];
repeated ProviderConsumerEpochCuGenesis provider_consumer_epoch_cus = 11 [(gogoproto.nullable) = false];
repeated ReputationGenesis reputations = 12 [(gogoproto.nullable) = false];
lavanet.lava.fixationstore.GenesisState reputation_scores = 13 [(gogoproto.nullable) = false];
// this line is used by starport scaffolding # genesis/proto/state
}

Expand Down Expand Up @@ -60,4 +60,11 @@ message ProviderConsumerEpochCuGenesis {
string project = 3;
string chain_id = 4;
ProviderConsumerEpochCu provider_consumer_epoch_cu = 5 [(gogoproto.nullable) = false];
}

message ReputationGenesis {
string chain_id = 1;
string cluster = 2;
string provider = 3;
Reputation reputation = 4 [(gogoproto.nullable) = false];
}
44 changes: 44 additions & 0 deletions proto/lavanet/lava/pairing/reputation.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
syntax = "proto3";
package lavanet.lava.pairing;

option go_package = "github.com/lavanet/lava/v3/x/pairing/types";
import "gogoproto/gogo.proto";
import "cosmos/base/v1beta1/coin.proto";

// Frac is a fracture struct that helps calculating and updating weighted average on the go
message Frac {
string num = 1 [(gogoproto.moretags) = "yaml:\"num\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false]; // weighted average numerator (w1*s1+w2*s2+...)
string denom = 2 [(gogoproto.moretags) = "yaml:\"denom\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false]; // weighted denominator (w1+w2+...)
}

// QosScore holds the QoS score from a QoS excellence report. The score and its variance are updated over time using a weighted average.
// Currently, the weight is the amount of relays that are associated with the QoS report.
message QosScore {
Frac score = 1 [(gogoproto.nullable) = false];
Frac variance = 2 [(gogoproto.nullable) = false];
}

// Reputation keeps the QosScore of a provider for a specific cluster for in the provider's geolocation.
// The store key is provider+chain+cluster.
// The epoch_score is a QosScore object that is aggregated over an epoch. When an epoch ends, the "score" field is updated
// with the epoch_score and the epoch_score is reset.
// The time_last_updated is used to calculate the appropriate time decay upon update.
// The creation_time is used to determine if the variance stabilization period has passed and score can be truncated.
// The stake is used when converting the reputation QoS scores to repuatation pairing score.
message Reputation {
QosScore score = 1 [(gogoproto.nullable) = false];
QosScore epoch_score = 2 [(gogoproto.nullable) = false];
int64 time_last_updated = 3;
int64 creation_time = 4;
cosmos.base.v1beta1.Coin stake = 5 [(gogoproto.nullable) = false];
}

// ReputationPairingScore holds the reputation pairing score used by the reputation pairing requirement.
// The score is ranged between [0.5-2]. It's kept in the reputations fixation store with a provider+chain+cluster key.
message ReputationPairingScore {
string score = 1 [(gogoproto.moretags) = "yaml:\"score\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false];
}

6 changes: 3 additions & 3 deletions testutil/e2e/protocolE2E.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,10 +676,10 @@ func restTests(rpcURL string, testDuration time.Duration) error {
errors := []string{}
mostImportantApisToTest := []string{
"%s/cosmos/base/tendermint/v1beta1/blocks/latest",
"%s/lavanet/lava/pairing/providers/LAV1",
"%s/lavanet/lava/pairing/clients/LAV1",
"%s/lavanet/lava/v2/pairing/providers/LAV1",
"%s/lavanet/lava/v2/pairing/clients/LAV1",
"%s/cosmos/gov/v1beta1/proposals",
"%s/lavanet/lava/spec/spec",
"%s/lavanet/lava/v2/spec/spec",
"%s/cosmos/base/tendermint/v1beta1/blocks/1",
}
for start := time.Now(); time.Since(start) < testDuration; {
Expand Down
12 changes: 7 additions & 5 deletions x/pairing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The pairing module is one of Lava's core modules and is closely connected to the
* [Filters](#filters)
* [Scores](#scores)
* [Quality Of Service](#quality-of-service)
* [Reputation](#reputation)
* [Passable QoS](#passable-qos)
* [Pairing Verification](#pairing-verification)
* [Unresponsiveness](#unresponsiveness)
* [Static Providers](#static-providers)
Expand Down Expand Up @@ -187,17 +189,17 @@ Finally, we calculate the score of each provider for a specific slot and select

#### Quality Of Service

##### Excellence QoS
##### Reputation

The Lava Network places a strong emphasis on delivering exceptional Quality of Service (QoS) to its consumers. To ensure this, consumers actively participate in monitoring and customizing their QoS metrics. They gauge provider performance by measuring latency in provider responses relative to a benchmark, assessing data freshness in comparison to the fastest provider, and evaluating the percentage of error or timeout responses in the availability metric. These scores are diligently recorded and sent on-chain alongside the relay proofs of service, creating a transparent and accountable system.
The Lava Network places a strong emphasis on delivering exceptional Quality of Service (QoS) to its consumers. To ensure this, consumers actively participate in monitoring and customizing their QoS excellence metrics. They gauge provider performance by measuring latency in provider responses relative to a benchmark, assessing data freshness in comparison to the fastest provider, and evaluating the percentage of error or timeout responses in the availability metric. These scores are diligently recorded and sent on-chain alongside the relay proofs of service, creating a transparent and accountable system. The provider's performance metric is called "Reputation". Higher reputation indicates higher QoS scores.

To further enhance the integrity of the QoS scores, updates are aggregated across all consumers in a manner that safeguards against false reports. Negative reports are weighted by usage, meaning that a consumer must actively use and pay a provider to diminish their QoS score. This mechanism discourages users from artificially lowering a provider's score.

These QoS excellence metrics only affect pairings and are aggregated over time with a decay function that favors the latest data, meaning providers can improve, and those providers that their service fails will be impacted to affect fewer users. This approach ensures that the QoS system remains dynamic and responsive, benefiting providers striving to enhance their services while minimizing the impact of service failures on a broader scale.
The Reputation metric only affect pairings and is aggregated over time with a decay function that favors the latest data, meaning providers can improve, and those providers that their service fails will be impacted to affect fewer users. This approach ensures that the reputation system remains dynamic and responsive, benefiting providers striving to enhance their services while minimizing the impact of service failures on a broader scale.

##### QoS
##### Passable QoS

In the Lava Network, alongside the comprehensive Quality of Service of Excellence metrics, there exists an additional metric known as Passable QoS. Unlike Excellence QoS, which offers a broad range of values, Passable QoS operates on a binary scale, either assigning a value of 0 or 1, averaged over relays. This metric simplifies the evaluation of service quality to a binary determination, indicating whether a relay meets the Passable QoS threshold, meaning it provides a level of service deemed acceptable for use.
In the Lava Network, alongside the comprehensive Reputation metric (which is calculated using QoS excellence reports), there exists an additional metric known as Passable QoS. Unlike Reputation, which offers a broad range of values, Passable QoS operates on a binary scale, either assigning a value of 0 or 1, averaged over relays. This metric simplifies the evaluation of service quality to a binary determination, indicating whether a relay meets the Passable QoS threshold, meaning it provides a level of service deemed acceptable for use.

The Passable QoS score directly influences the total payout for a specific payment; however, it's important to note that only 50% of the payout is exposed to this metric (can be changed via governance). This allocation ensures a balance between incentivizing excellent service and discouraging poor performance.

Expand Down
9 changes: 7 additions & 2 deletions x/pairing/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState)
for _, elem := range genState.BadgeUsedCuList {
k.SetBadgeUsedCu(ctx, elem)
}
// Set all the reputations
for _, elem := range genState.Reputations {
k.SetReputation(ctx, elem.ChainId, elem.Cluster, elem.Provider, elem.Reputation)
}

k.InitBadgeTimers(ctx, genState.BadgesTS)
k.InitProviderQoS(ctx, genState.ProviderQosFS)
k.InitReputations(ctx, genState.ReputationScores)
// this line is used by starport scaffolding # genesis/module/init
k.SetParams(ctx, genState.Params)
}
Expand All @@ -45,8 +49,9 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
genesis.ProviderEpochComplainedCus = k.GetAllProviderEpochComplainerCuStore(ctx)
genesis.ProviderConsumerEpochCus = k.GetAllProviderConsumerEpochCuStore(ctx)
genesis.BadgeUsedCuList = k.GetAllBadgeUsedCu(ctx)
genesis.Reputations = k.GetAllReputation(ctx)
genesis.BadgesTS = k.ExportBadgesTimers(ctx)
genesis.ProviderQosFS = k.ExportProviderQoS(ctx)
genesis.ReputationScores = k.ExportReputations(ctx)
// this line is used by starport scaffolding # genesis/module/export

return genesis
Expand Down
15 changes: 15 additions & 0 deletions x/pairing/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ func TestGenesis(t *testing.T) {
BadgeUsedCuKey: []byte{byte(1)},
},
},
Reputations: []types.ReputationGenesis{
{
ChainId: "0",
Cluster: "0",
Provider: "0",
Reputation: types.Reputation{},
},
{
ChainId: "1",
Cluster: "1",
Provider: "1",
Reputation: types.Reputation{},
},
},
// this line is used by starport scaffolding # genesis/test/state
}

Expand All @@ -97,5 +111,6 @@ func TestGenesis(t *testing.T) {
require.ElementsMatch(t, genesisState.ProviderEpochCus, got.ProviderEpochCus)
require.ElementsMatch(t, genesisState.ProviderConsumerEpochCus, got.ProviderConsumerEpochCus)
require.ElementsMatch(t, genesisState.BadgeUsedCuList, got.BadgeUsedCuList)
require.ElementsMatch(t, genesisState.Reputations, got.Reputations)
// this line is used by starport scaffolding # genesis/test/assert
}
31 changes: 24 additions & 7 deletions x/pairing/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package keeper
import (
"fmt"

"cosmossdk.io/collections"
collcompat "github.com/lavanet/lava/v3/utils/collcompat"

storetypes "github.com/cosmos/cosmos-sdk/store/types"
epochstoragetypes "github.com/lavanet/lava/v3/x/epochstorage/types"
timerstoretypes "github.com/lavanet/lava/v3/x/timerstore/types"
Expand Down Expand Up @@ -31,11 +34,13 @@ type (
subscriptionKeeper types.SubscriptionKeeper
planKeeper types.PlanKeeper
badgeTimerStore timerstoretypes.TimerStore
providerQosFS fixationtypes.FixationStore
reputationsFS fixationtypes.FixationStore
downtimeKeeper types.DowntimeKeeper
dualstakingKeeper types.DualstakingKeeper
stakingKeeper types.StakingKeeper

schema collections.Schema
reputations collections.Map[collections.Triple[string, string, string], types.Reputation] // save qos info per provider, chain and cluster
pairingQueryCache *map[string][]epochstoragetypes.StakeEntry
}
)
Expand Down Expand Up @@ -74,6 +79,7 @@ func NewKeeper(
ps = ps.WithKeyTable(types.ParamKeyTable())
}

sb := collections.NewSchemaBuilder(collcompat.NewKVStoreService(storeKey))
emptypairingQueryCache := map[string][]epochstoragetypes.StakeEntry{}

keeper := &Keeper{
Expand All @@ -91,7 +97,12 @@ func NewKeeper(
downtimeKeeper: downtimeKeeper,
dualstakingKeeper: dualstakingKeeper,
stakingKeeper: stakingKeeper,
pairingQueryCache: &emptypairingQueryCache,

reputations: collections.NewMap(sb, types.ReputationPrefix, "reputations",
collections.TripleKeyCodec(collections.StringKey, collections.StringKey, collections.StringKey),
collcompat.ProtoValue[types.Reputation](cdc),
),
pairingQueryCache: &emptypairingQueryCache,
}

// note that the timer and badgeUsedCu keys are the same (so we can use only the second arg)
Expand All @@ -102,7 +113,13 @@ func NewKeeper(
WithCallbackByBlockHeight(badgeTimerCallback)
keeper.badgeTimerStore = *badgeTimerStore

keeper.providerQosFS = *fixationStoreKeeper.NewFixationStore(storeKey, types.ProviderQosStorePrefix)
keeper.reputationsFS = *fixationStoreKeeper.NewFixationStore(storeKey, types.ProviderQosStorePrefix)

schema, err := sb.Build()
if err != nil {
panic(err)
}
keeper.schema = schema

return keeper
}
Expand All @@ -129,10 +146,10 @@ func (k Keeper) EndBlock(ctx sdk.Context) {
k.ResetPairingRelayCache(ctx)
}

func (k Keeper) InitProviderQoS(ctx sdk.Context, gs fixationtypes.GenesisState) {
k.providerQosFS.Init(ctx, gs)
func (k Keeper) InitReputations(ctx sdk.Context, gs fixationtypes.GenesisState) {
k.reputationsFS.Init(ctx, gs)
}

func (k Keeper) ExportProviderQoS(ctx sdk.Context) fixationtypes.GenesisState {
return k.providerQosFS.Export(ctx)
func (k Keeper) ExportReputations(ctx sdk.Context) fixationtypes.GenesisState {
return k.reputationsFS.Export(ctx)
}
26 changes: 0 additions & 26 deletions x/pairing/keeper/qos_excellence.go

This file was deleted.

Loading
Loading