diff --git a/config/provider_examples/provider_example_cookbook/eth_provider_with_archive_debug_trace.yml b/config/provider_examples/provider_example_cookbook/eth_provider_with_archive_debug_trace.yml index 8c772143bb..a91298b3cf 100644 --- a/config/provider_examples/provider_example_cookbook/eth_provider_with_archive_debug_trace.yml +++ b/config/provider_examples/provider_example_cookbook/eth_provider_with_archive_debug_trace.yml @@ -2,6 +2,8 @@ # - archive # - debug # - trace +# - trace + archive +# - debug + archive # ** Replace the urls with your own node urls ** @@ -29,4 +31,20 @@ endpoints: - url: ws://my-eth-node.com/eth/archive/ws addons: - trace + - url: http://my-eth-node.com/eth/http/archive + addons: + - debug + - archive + - url: ws://my-eth-node.com/eth/archive/ws + addons: + - debug + - archive + - url: http://my-eth-node.com/eth/http/archive + addons: + - trace + - archive + - url: ws://my-eth-node.com/eth/archive/ws + addons: + - trace + - archive \ No newline at end of file diff --git a/go.mod b/go.mod index 9f16e3b357..80bb9793f0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/lavanet/lava/v4 -go 1.23 +go 1.23.3 require ( github.com/99designs/keyring v1.2.1 // indirect diff --git a/proto/lavanet/lava/pairing/genesis.proto b/proto/lavanet/lava/pairing/genesis.proto index 5d6ba2868b..c44551fd42 100644 --- a/proto/lavanet/lava/pairing/genesis.proto +++ b/proto/lavanet/lava/pairing/genesis.proto @@ -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 @@ -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 } @@ -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]; } \ No newline at end of file diff --git a/proto/lavanet/lava/pairing/params.proto b/proto/lavanet/lava/pairing/params.proto index 5a082cb6f6..ba9dd23175 100644 --- a/proto/lavanet/lava/pairing/params.proto +++ b/proto/lavanet/lava/pairing/params.proto @@ -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\""]; } \ No newline at end of file diff --git a/proto/lavanet/lava/pairing/query.proto b/proto/lavanet/lava/pairing/query.proto index 0f806dd49e..0d282d9794 100644 --- a/proto/lavanet/lava/pairing/query.proto +++ b/proto/lavanet/lava/pairing/query.proto @@ -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"; @@ -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 @@ -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]; } \ No newline at end of file diff --git a/proto/lavanet/lava/pairing/relay.proto b/proto/lavanet/lava/pairing/relay.proto index a74bd761ee..09b0f0a6d0 100644 --- a/proto/lavanet/lava/pairing/relay.proto +++ b/proto/lavanet/lava/pairing/relay.proto @@ -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", diff --git a/proto/lavanet/lava/pairing/reputation.proto b/proto/lavanet/lava/pairing/reputation.proto new file mode 100644 index 0000000000..55afa4b7e7 --- /dev/null +++ b/proto/lavanet/lava/pairing/reputation.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; +package lavanet.lava.pairing; + +option go_package = "github.com/lavanet/lava/v4/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]; +} + diff --git a/protocol/chainlib/chain_router_test.go b/protocol/chainlib/chain_router_test.go index db1b9fa8d6..e2fe5e1c0a 100644 --- a/protocol/chainlib/chain_router_test.go +++ b/protocol/chainlib/chain_router_test.go @@ -1175,8 +1175,8 @@ func TestMain(m *testing.M) { listener := createRPCServer() for { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - _, err := rpcclient.DialContext(ctx, listenerAddressHttp) - _, err2 := rpcclient.DialContext(ctx, listenerAddressWs) + _, err := rpcclient.DialContext(ctx, listenerAddressHttp, nil) + _, err2 := rpcclient.DialContext(ctx, listenerAddressWs, nil) if err2 != nil { utils.LavaFormatDebug("waiting for grpc server to launch") continue diff --git a/protocol/chainlib/chainproxy/connector.go b/protocol/chainlib/chainproxy/connector.go index 307f86915a..f5c7d51ff7 100644 --- a/protocol/chainlib/chainproxy/connector.go +++ b/protocol/chainlib/chainproxy/connector.go @@ -107,6 +107,18 @@ func (connector *Connector) numberOfUsedClients() int { return int(atomic.LoadInt64(&connector.usedClients)) } +func (connector *Connector) getRpcClient(ctx context.Context, nodeUrl common.NodeUrl) (*rpcclient.Client, error) { + authPathNodeUrl := nodeUrl.AuthConfig.AddAuthPath(nodeUrl.Url) + // origin used for auth header in the websocket case + authHeaders := nodeUrl.GetAuthHeaders() + rpcClient, err := rpcclient.DialContext(ctx, authPathNodeUrl, authHeaders) + if err != nil { + return nil, err + } + nodeUrl.SetAuthHeaders(ctx, rpcClient.SetHeader) + return rpcClient, nil +} + func (connector *Connector) createConnection(ctx context.Context, nodeUrl common.NodeUrl, currentNumberOfConnections int) (*rpcclient.Client, error) { var rpcClient *rpcclient.Client var err error @@ -124,21 +136,13 @@ func (connector *Connector) createConnection(ctx context.Context, nodeUrl common } timeout := common.AverageWorldLatency * (1 + time.Duration(numberOfConnectionAttempts)) nctx, cancel := nodeUrl.LowerContextTimeoutWithDuration(ctx, timeout) - // add auth path - authPathNodeUrl := nodeUrl.AuthConfig.AddAuthPath(nodeUrl.Url) - rpcClient, err = rpcclient.DialContext(nctx, authPathNodeUrl) + // get rpcClient + rpcClient, err = connector.getRpcClient(nctx, nodeUrl) if err != nil { - utils.LavaFormatWarning("Could not connect to the node, retrying", err, []utils.Attribute{ - {Key: "Current Number Of Connections", Value: currentNumberOfConnections}, - {Key: "Network Address", Value: authPathNodeUrl}, - {Key: "Number Of Attempts", Value: numberOfConnectionAttempts}, - {Key: "timeout", Value: timeout}, - }...) cancel() continue } cancel() - nodeUrl.SetAuthHeaders(ctx, rpcClient.SetHeader) break } @@ -178,7 +182,8 @@ func (connector *Connector) increaseNumberOfClients(ctx context.Context, numberO var err error for connectionAttempt := 0; connectionAttempt < MaximumNumberOfParallelConnectionsAttempts; connectionAttempt++ { nctx, cancel := connector.nodeUrl.LowerContextTimeoutWithDuration(ctx, common.AverageWorldLatency*2) - rpcClient, err = rpcclient.DialContext(nctx, connector.nodeUrl.Url) + // get rpcClient + rpcClient, err = connector.getRpcClient(nctx, connector.nodeUrl) if err != nil { utils.LavaFormatDebug( "could no increase number of connections to the node jsonrpc connector, retrying", diff --git a/protocol/chainlib/chainproxy/connector_test.go b/protocol/chainlib/chainproxy/connector_test.go index e408ae2062..26a8100425 100644 --- a/protocol/chainlib/chainproxy/connector_test.go +++ b/protocol/chainlib/chainproxy/connector_test.go @@ -2,6 +2,7 @@ package chainproxy import ( "context" + "encoding/json" "fmt" "log" "net" @@ -16,6 +17,7 @@ import ( "github.com/lavanet/lava/v4/utils" pb_pkg "github.com/lavanet/lava/v4/x/spec/types" "github.com/stretchr/testify/require" + "golang.org/x/net/websocket" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) @@ -185,7 +187,7 @@ func TestMain(m *testing.M) { listener := createRPCServer() for { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - _, err := rpcclient.DialContext(ctx, listenerAddressTcp) + _, err := rpcclient.DialContext(ctx, listenerAddressTcp, nil) if err != nil { utils.LavaFormatDebug("waiting for grpc server to launch") continue @@ -199,3 +201,89 @@ func TestMain(m *testing.M) { listener.Close() os.Exit(code) } + +func TestConnectorWebsocket(t *testing.T) { + // Set up auth headers we expect + expectedAuthHeader := "Bearer test-token" + + // Create WebSocket server with auth check + srv := &http.Server{ + Addr: "localhost:0", // random available port + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Check auth header + authHeader := r.Header.Get("Authorization") + if authHeader != expectedAuthHeader { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + fmt.Println("connection OK!") + // Upgrade to websocket + upgrader := websocket.Server{ + Handler: websocket.Handler(func(ws *websocket.Conn) { + defer ws.Close() + // Simple echo server + for { + var msg string + err := websocket.Message.Receive(ws, &msg) + if err != nil { + break + } + websocket.Message.Send(ws, msg) + } + }), + } + upgrader.ServeHTTP(w, r) + }), + } + + // Start server + listener, err := net.Listen("tcp", "localhost:0") + require.NoError(t, err) + defer listener.Close() + + go srv.Serve(listener) + wsURL := "ws://" + listener.Addr().String() + + // Create connector with auth config + ctx := context.Background() + nodeUrl := common.NodeUrl{ + Url: wsURL, + AuthConfig: common.AuthConfig{ + AuthHeaders: map[string]string{ + "Authorization": expectedAuthHeader, + }, + }, + } + + // Create connector + conn, err := NewConnector(ctx, numberOfClients, nodeUrl) + require.NoError(t, err) + defer conn.Close() + + // Wait for connections to be established + for { + if len(conn.freeClients) == numberOfClients { + break + } + time.Sleep(10 * time.Millisecond) + } + + // Get a client and test the connection + client, err := conn.GetRpc(ctx, true) + require.NoError(t, err) + + // Test sending a message using CallContext + params := map[string]interface{}{ + "test": "value", + } + id := json.RawMessage(`1`) + _, err = client.CallContext(ctx, id, "test_method", params, true, true) + require.NoError(t, err) + + // Return the client + conn.ReturnRpc(client) + + // Verify connection pool state + require.Equal(t, int64(0), conn.usedClients) + require.Equal(t, numberOfClients, len(conn.freeClients)) +} diff --git a/protocol/chainlib/chainproxy/rpcclient/client.go b/protocol/chainlib/chainproxy/rpcclient/client.go index c745f82ee2..15fcdb9b73 100644 --- a/protocol/chainlib/chainproxy/rpcclient/client.go +++ b/protocol/chainlib/chainproxy/rpcclient/client.go @@ -177,14 +177,14 @@ func (op *requestOp) wait(ctx context.Context, c *Client) (*JsonrpcMessage, erro // // The client reconnects automatically if the connection is lost. func Dial(rawurl string) (*Client, error) { - return DialContext(context.Background(), rawurl) + return DialContext(context.Background(), rawurl, nil) } // DialContext creates a new RPC client, just like Dial. // // The context is used to cancel or time out the initial connection establishment. It does // not affect subsequent interactions with the client. -func DialContext(ctx context.Context, rawurl string) (*Client, error) { +func DialContext(ctx context.Context, rawurl string, wsHeaders map[string]string) (*Client, error) { u, err := url.Parse(rawurl) if err != nil { return nil, err @@ -193,7 +193,7 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) { case "http", "https": return DialHTTP(rawurl) case "ws", "wss": - return DialWebsocket(ctx, rawurl, "") + return DialWebsocket(ctx, rawurl, wsHeaders) case "stdio": return DialStdIO(ctx) case "": diff --git a/protocol/chainlib/chainproxy/rpcclient/websocket.go b/protocol/chainlib/chainproxy/rpcclient/websocket.go index 81566ffe0f..680d37167a 100755 --- a/protocol/chainlib/chainproxy/rpcclient/websocket.go +++ b/protocol/chainlib/chainproxy/rpcclient/websocket.go @@ -187,8 +187,8 @@ func parseOriginURL(origin string) (string, string, string, error) { // DialWebsocketWithDialer creates a new RPC client that communicates with a JSON-RPC server // that is listening on the given endpoint using the provided dialer. -func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, dialer websocket.Dialer) (*Client, error) { - endpoint, header, err := wsClientHeaders(endpoint, origin) +func DialWebsocketWithDialer(ctx context.Context, endpoint string, dialer websocket.Dialer, headers map[string]string) (*Client, error) { + endpoint, header, err := wsClientHeaders(endpoint, headers) if err != nil { return nil, err } @@ -210,23 +210,23 @@ func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, diale // // The context is used for the initial connection establishment. It does not // affect subsequent interactions with the client. -func DialWebsocket(ctx context.Context, endpoint, origin string) (*Client, error) { +func DialWebsocket(ctx context.Context, endpoint string, headers map[string]string) (*Client, error) { dialer := websocket.Dialer{ ReadBufferSize: wsReadBuffer, WriteBufferSize: wsWriteBuffer, WriteBufferPool: wsBufferPool, } - return DialWebsocketWithDialer(ctx, endpoint, origin, dialer) + return DialWebsocketWithDialer(ctx, endpoint, dialer, headers) } -func wsClientHeaders(endpoint, origin string) (string, http.Header, error) { +func wsClientHeaders(endpoint string, headers map[string]string) (string, http.Header, error) { endpointURL, err := url.Parse(endpoint) if err != nil { return endpoint, nil, err } header := make(http.Header) - if origin != "" { - header.Add("origin", origin) + for headerKey, headerValue := range headers { + header.Add(headerKey, headerValue) } if endpointURL.User != nil { b64auth := base64.StdEncoding.EncodeToString([]byte(endpointURL.User.String())) diff --git a/protocol/common/endpoints.go b/protocol/common/endpoints.go index 2379512708..098b301b57 100644 --- a/protocol/common/endpoints.go +++ b/protocol/common/endpoints.go @@ -91,6 +91,10 @@ func (nurl *NodeUrl) UrlStr() string { return parsedURL.String() } +func (url *NodeUrl) GetAuthHeaders() map[string]string { + return url.AuthConfig.AuthHeaders +} + func (url *NodeUrl) SetAuthHeaders(ctx context.Context, headerSetter func(string, string)) { for header, headerValue := range url.AuthConfig.AuthHeaders { headerSetter(header, headerValue) diff --git a/protocol/lavaprotocol/request_builder.go b/protocol/lavaprotocol/request_builder.go index 194d04aba8..aa96ee8730 100644 --- a/protocol/lavaprotocol/request_builder.go +++ b/protocol/lavaprotocol/request_builder.go @@ -74,6 +74,9 @@ func ConstructRelaySession(lavaChainID string, relayRequestData *pairingtypes.Re copiedQOS := copyQoSServiceReport(singleConsumerSession.QoSInfo.LastQoSReport) copiedExcellenceQOS := copyQoSServiceReport(singleConsumerSession.QoSInfo.LastExcellenceQoSReportRaw) // copy raw report for the node + // validate and fix QoS excellence report before sending it to the node + copiedExcellenceQOS.ValidateAndFixQoSExcellence() + return &pairingtypes.RelaySession{ SpecId: chainID, ContentHash: sigs.HashMsg(relayRequestData.GetContentHashData()), diff --git a/protocol/lavasession/consumer_session_manager.go b/protocol/lavasession/consumer_session_manager.go index c1e179edde..deaf3278eb 100644 --- a/protocol/lavasession/consumer_session_manager.go +++ b/protocol/lavasession/consumer_session_manager.go @@ -102,6 +102,9 @@ func (csm *ConsumerSessionManager) UpdateAllProviders(epoch uint64, pairingList csm.reportedProviders.Reset() csm.pairingAddressesLength = uint64(pairingListLength) csm.numberOfResets = 0 + + providerAddressToEndpoint := map[string]string{} + csm.RemoveAddonAddresses("", nil) // Reset the pairingPurge. // This happens only after an entire epoch. so its impossible to have session connected to the old purged list @@ -111,11 +114,13 @@ func (csm *ConsumerSessionManager) UpdateAllProviders(epoch uint64, pairingList for idx, provider := range pairingList { csm.pairingAddresses[idx] = provider.PublicLavaAddress csm.pairing[provider.PublicLavaAddress] = provider + providerAddressToEndpoint[provider.PublicLavaAddress] = provider.Endpoints[0].NetworkAddress } csm.setValidAddressesToDefaultValue("", nil) // the starting point is that valid addresses are equal to pairing addresses. // reset session related metrics go csm.consumerMetricsManager.ResetSessionRelatedMetrics() go csm.providerOptimizer.UpdateWeights(CalcWeightsByStake(pairingList), epoch) + go csm.consumerMetricsManager.ResetBlockedProvidersMetrics(csm.rpcEndpoint.ChainID, csm.rpcEndpoint.ApiInterface, providerAddressToEndpoint) utils.LavaFormatDebug("updated providers", utils.Attribute{Key: "epoch", Value: epoch}, utils.Attribute{Key: "spec", Value: csm.rpcEndpoint.Key()}) return nil @@ -830,6 +835,13 @@ func (csm *ConsumerSessionManager) removeAddressFromValidAddresses(address strin csm.currentlyBlockedProviderAddresses = append(csm.currentlyBlockedProviderAddresses, address) // sort the blocked provider list by cu served csm.sortBlockedProviderListByCuServed() + provider, ok := csm.pairing[addr] + if ok { + info := csm.RPCEndpoint() + go func(networkAddress string, chainId string, apiInterface string, providerAddress string) { + csm.consumerMetricsManager.SetBlockedProvider(chainId, apiInterface, providerAddress, networkAddress, true) + }(provider.Endpoints[0].NetworkAddress, info.ChainID, info.ApiInterface, addr) + } return nil } } @@ -1005,7 +1017,11 @@ func (csm *ConsumerSessionManager) validateAndReturnBlockedProviderToValidAddres csm.RemoveAddonAddresses("", nil) // Reset redemption status if provider, ok := csm.pairing[providerAddress]; ok { + info := csm.RPCEndpoint() provider.atomicWriteBlockedStatus(BlockedProviderSessionUnusedStatus) + go func(networkAddress string, chainId string, apiInterface string, providerAddress string) { + csm.consumerMetricsManager.SetBlockedProvider(chainId, apiInterface, providerAddress, networkAddress, false) + }(provider.Endpoints[0].NetworkAddress, info.ChainID, info.ApiInterface, providerAddress) } return } diff --git a/protocol/metrics/consumer_metrics_manager.go b/protocol/metrics/consumer_metrics_manager.go index bb17287a47..fe2487e389 100644 --- a/protocol/metrics/consumer_metrics_manager.go +++ b/protocol/metrics/consumer_metrics_manager.go @@ -54,6 +54,7 @@ type ConsumerMetricsManager struct { latencyMetric *prometheus.GaugeVec qosMetric *MappedLabelsGaugeVec qosExcellenceMetric *MappedLabelsGaugeVec + blockedProviderMetric *MappedLabelsGaugeVec LatestBlockMetric *MappedLabelsGaugeVec LatestProviderRelay *prometheus.GaugeVec virtualEpochMetric *prometheus.GaugeVec @@ -153,6 +154,17 @@ func NewConsumerMetricsManager(options ConsumerMetricsManagerOptions) *ConsumerM Help: "The latency of requests requested by the consumer over time.", }, []string{"spec", "apiInterface"}) + blockedProviderMetricLabels := []string{"spec", "apiInterface", "provider_address"} + if ShowProviderEndpointInMetrics { + blockedProviderMetricLabels = append(blockedProviderMetricLabels, "provider_endpoint") + } + + blockedProviderMetric := NewMappedLabelsGaugeVec(MappedLabelsMetricOpts{ + Name: "lava_consumer_provider_blocked", + Help: "Is provider blocked. 1-blocked, 0-not blocked", + Labels: blockedProviderMetricLabels, + }) + qosMetricLabels := []string{"spec", "apiInterface", "provider_address", "qos_metric"} if ShowProviderEndpointInMetrics { qosMetricLabels = append(qosMetricLabels, "provider_endpoint") @@ -290,6 +302,7 @@ func NewConsumerMetricsManager(options ConsumerMetricsManagerOptions) *ConsumerM latencyMetric: latencyMetric, qosMetric: qosMetric, qosExcellenceMetric: qosExcellenceMetric, + blockedProviderMetric: blockedProviderMetric, LatestBlockMetric: latestBlockMetric, LatestProviderRelay: latestProviderRelay, providerRelays: map[string]uint64{}, @@ -569,6 +582,19 @@ func (pme *ConsumerMetricsManager) ResetSessionRelatedMetrics() { pme.providerRelays = map[string]uint64{} } +func (pme *ConsumerMetricsManager) ResetBlockedProvidersMetrics(chainId, apiInterface string, providers map[string]string) { + if pme == nil { + return + } + pme.lock.Lock() + defer pme.lock.Unlock() + pme.blockedProviderMetric.Reset() + for provider, endpoint := range providers { + labels := map[string]string{"spec": chainId, "apiInterface": apiInterface, "provider_address": provider, "provider_endpoint": endpoint} + pme.blockedProviderMetric.WithLabelValues(labels).Set(0) + } +} + func (pme *ConsumerMetricsManager) SetVersion(version string) { if pme == nil { return @@ -627,6 +653,20 @@ func (pme *ConsumerMetricsManager) SetLoLResponse(success bool) { } } +func (pme *ConsumerMetricsManager) SetBlockedProvider(chainId, apiInterface, providerAddress, providerEndpoint string, isBlocked bool) { + if pme == nil { + return + } + var value float64 = 0 + if isBlocked { + value = 1 + } + labels := map[string]string{"spec": chainId, "apiInterface": apiInterface, "provider_address": providerAddress, "provider_endpoint": providerEndpoint} + pme.lock.Lock() + defer pme.lock.Unlock() + pme.blockedProviderMetric.WithLabelValues(labels).Set(value) +} + func (pme *ConsumerMetricsManager) handleOptimizerQoS(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) diff --git a/scripts/test/cli_test.sh b/scripts/test/cli_test.sh index 8030150401..ef23326655 100755 --- a/scripts/test/cli_test.sh +++ b/scripts/test/cli_test.sh @@ -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 diff --git a/testutil/common/tester.go b/testutil/common/tester.go index 55bcf924ea..a0a3dddac8 100644 --- a/testutil/common/tester.go +++ b/testutil/common/tester.go @@ -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{ @@ -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: " + diff --git a/utils/convert.go b/utils/convert.go index 0a39cc7093..8ec088a7cd 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -1,5 +1,14 @@ 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 diff --git a/utils/math.go b/utils/math.go index ad8d06a39b..0631937ab6 100644 --- a/utils/math.go +++ b/utils/math.go @@ -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.718281828459045235" +) func Min[T constraints.Ordered](x, y T) T { if x < y { @@ -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: Take the bth root of e + eRoot, err := e.ApproxRoot(denominatorUint64) + if err != nil { + panic(err) + } + + // Step 2: Calculate (e^(1/b))^a + result := eRoot.Power(numeratorUint64) + + if negative { + // Step 3: Take the reciprocal + result = sdk.OneDec().Quo(result) + } + + return result +} diff --git a/x/pairing/README.md b/x/pairing/README.md index 39c95109b5..42f351897f 100644 --- a/x/pairing/README.md +++ b/x/pairing/README.md @@ -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) @@ -197,17 +199,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. @@ -350,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 | diff --git a/x/pairing/client/cli/query.go b/x/pairing/client/cli/query.go index 69e17c6c8d..4a39ea03d5 100644 --- a/x/pairing/client/cli/query.go +++ b/x/pairing/client/cli/query.go @@ -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()) diff --git a/x/pairing/client/cli/query_provider_reputation.go b/x/pairing/client/cli/query_provider_reputation.go new file mode 100644 index 0000000000..526a2b64b3 --- /dev/null +++ b/x/pairing/client/cli/query_provider_reputation.go @@ -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 +} diff --git a/x/pairing/client/cli/query_provider_reputation_details.go b/x/pairing/client/cli/query_provider_reputation_details.go new file mode 100644 index 0000000000..9d866fb45f --- /dev/null +++ b/x/pairing/client/cli/query_provider_reputation_details.go @@ -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 +} diff --git a/x/pairing/client/cli/query_providers_epoch_cu.go b/x/pairing/client/cli/query_providers_epoch_cu.go index a79de4fa72..4cd7e127ed 100644 --- a/x/pairing/client/cli/query_providers_epoch_cu.go +++ b/x/pairing/client/cli/query_providers_epoch_cu.go @@ -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 { diff --git a/x/pairing/genesis.go b/x/pairing/genesis.go index 595cbb5118..7e506cb499 100644 --- a/x/pairing/genesis.go +++ b/x/pairing/genesis.go @@ -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) } @@ -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 diff --git a/x/pairing/genesis_test.go b/x/pairing/genesis_test.go index 5424e4d5ed..ca0c6a0bf9 100644 --- a/x/pairing/genesis_test.go +++ b/x/pairing/genesis_test.go @@ -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 } @@ -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 } diff --git a/x/pairing/keeper/filters/filter.go b/x/pairing/keeper/filters/filter.go index 1ec9953132..e89bc5c376 100644 --- a/x/pairing/keeper/filters/filter.go +++ b/x/pairing/keeper/filters/filter.go @@ -60,7 +60,7 @@ func initFilters(filters []Filter, strictestPolicy planstypes.Policy) (activeFil return activeFilters } -func SetupScores(ctx sdk.Context, filters []Filter, providers []epochstoragetypes.StakeEntry, strictestPolicy *planstypes.Policy, currentEpoch uint64, slotCount int, cluster string, qg pairingscores.QosGetter) ([]*pairingscores.PairingScore, error) { +func SetupScores(ctx sdk.Context, filters []Filter, providers []epochstoragetypes.StakeEntry, strictestPolicy *planstypes.Policy, currentEpoch uint64, slotCount int, cluster string, rg pairingscores.ReputationGetter) ([]*pairingscores.PairingScore, error) { filters = initFilters(filters, *strictestPolicy) var filtersResult [][]bool @@ -108,14 +108,11 @@ func SetupScores(ctx sdk.Context, filters []Filter, providers []epochstoragetype } if result { - // TODO: uncomment this code once the providerQosFS's update is implemented (it's currently always empty) - // qos, err := qg.GetQos(ctx, providers[j].Chain, cluster, providers[j].Address) - // if err != nil { - // // only printing error and skipping provider so pairing won't fail - // utils.LavaFormatError("could not construct provider qos", err) - // continue - // } - providerScore := pairingscores.NewPairingScore(&providers[j], types.QualityOfServiceReport{}) + reputationScore, _, found := rg.GetReputationScoreForBlock(ctx, providers[j].Chain, cluster, providers[j].Address, currentEpoch) + if !found { + reputationScore = types.DefaultReputationPairingScore + } + providerScore := pairingscores.NewPairingScore(&providers[j], reputationScore) providerScore.SlotFiltering = slotFiltering providerScores = append(providerScores, providerScore) } diff --git a/x/pairing/keeper/grpc_query_provider_reputation.go b/x/pairing/keeper/grpc_query_provider_reputation.go new file mode 100644 index 0000000000..ac65fb2484 --- /dev/null +++ b/x/pairing/keeper/grpc_query_provider_reputation.go @@ -0,0 +1,113 @@ +package keeper + +import ( + "context" + "fmt" + "sort" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v4/utils" + "github.com/lavanet/lava/v4/utils/lavaslices" + "github.com/lavanet/lava/v4/x/pairing/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + varianceThreshold = float64(1) // decides if the overall_performance field can be calculated + percentileRank = float64(0.8) // rank for percentile to decide whether the overall_performance is "good" or "bad" + goodScore = "good" + badScore = "bad" + lowVariance = "low_variance" +) + +func (k Keeper) ProviderReputation(goCtx context.Context, req *types.QueryProviderReputationRequest) (*types.QueryProviderReputationResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + chains := []string{req.ChainID} + if req.ChainID == "*" { + chains = k.specKeeper.GetAllChainIDs(ctx) + } + + clusters := []string{req.Cluster} + if req.Cluster == "*" { + clusters = k.subscriptionKeeper.GetAllClusters(ctx) + } + + // get all the reputation scores of the requested provider and gather valid chainID+cluster pairs + type chainClusterScore struct { + chainID string + cluster string + score math.LegacyDec + } + requestedProviderData := []chainClusterScore{} + for _, chainID := range chains { + for _, cluster := range clusters { + score, found := k.GetReputationScore(ctx, chainID, cluster, req.Provider) + if !found { + continue + } + requestedProviderData = append(requestedProviderData, chainClusterScore{chainID: chainID, cluster: cluster, score: score}) + } + } + + // get scores from other providers for the relevant chains and clusters + res := []types.ReputationData{} + for _, data := range requestedProviderData { + chainClusterRes := types.ReputationData{ChainID: data.chainID, Cluster: data.cluster} + + // get all reputation pairing score indices for a chainID+cluster pair + inds := k.reputationsFS.GetAllEntryIndicesWithPrefix(ctx, types.ReputationScoreKey(data.chainID, data.cluster, "")) + + // collect all pairing scores with indices and sort in ascending order + pairingScores := []float64{} + for _, ind := range inds { + var score types.ReputationPairingScore + found := k.reputationsFS.FindEntry(ctx, ind, uint64(ctx.BlockHeight()), &score) + if !found { + return nil, utils.LavaFormatError("invalid reputationFS state", fmt.Errorf("reputation pairing score not found"), + utils.LogAttr("index", ind), + utils.LogAttr("block", ctx.BlockHeight()), + ) + } + pairingScores = append(pairingScores, score.Score.MustFloat64()) + } + sort.Slice(pairingScores, func(i, j int) bool { + return pairingScores[i] < pairingScores[j] + }) + + // find the provider's rank + rank := len(pairingScores) + for i, score := range pairingScores { + if data.score.MustFloat64() <= score { + rank -= i + break + } + } + + // calculate the pairing scores variance + mean := lavaslices.Average(pairingScores) + variance := lavaslices.Variance(pairingScores, mean) + + // create the reputation data and append + chainClusterRes.Rank = uint64(rank) + chainClusterRes.Providers = uint64(len(pairingScores)) + if variance < varianceThreshold { + chainClusterRes.OverallPerformance = lowVariance + } else { + if pairingScores[len(pairingScores)-rank] > lavaslices.Percentile(pairingScores, percentileRank) { + chainClusterRes.OverallPerformance = goodScore + } else { + chainClusterRes.OverallPerformance = badScore + } + } + res = append(res, chainClusterRes) + } + + return &types.QueryProviderReputationResponse{Data: res}, nil +} diff --git a/x/pairing/keeper/grpc_query_provider_reputation_details.go b/x/pairing/keeper/grpc_query_provider_reputation_details.go new file mode 100644 index 0000000000..8fb6af5252 --- /dev/null +++ b/x/pairing/keeper/grpc_query_provider_reputation_details.go @@ -0,0 +1,46 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v4/x/pairing/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (k Keeper) ProviderReputationDetails(goCtx context.Context, req *types.QueryProviderReputationDetailsRequest) (*types.QueryProviderReputationDetailsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + chains := []string{req.ChainID} + if req.ChainID == "*" { + chains = k.specKeeper.GetAllChainIDs(ctx) + } + + clusters := []string{req.Cluster} + if req.Cluster == "*" { + clusters = k.subscriptionKeeper.GetAllClusters(ctx) + } + + // get all the reputations and reputation scores of the requested provider + res := []types.ReputationDevData{} + for _, chainID := range chains { + for _, cluster := range clusters { + chainClusterRes := types.ReputationDevData{ChainID: chainID, Cluster: cluster} + score, foundPairingScore := k.GetReputationScore(ctx, chainID, cluster, req.Address) + reputation, foundReputation := k.GetReputation(ctx, chainID, cluster, req.Address) + if !foundPairingScore || !foundReputation { + continue + } + chainClusterRes.Reputation = reputation + chainClusterRes.ReputationPairingScore = types.ReputationPairingScore{Score: score} + res = append(res, chainClusterRes) + } + } + + return &types.QueryProviderReputationDetailsResponse{Data: res}, nil +} diff --git a/x/pairing/keeper/grpc_query_provider_reputation_test.go b/x/pairing/keeper/grpc_query_provider_reputation_test.go new file mode 100644 index 0000000000..ad1e00463d --- /dev/null +++ b/x/pairing/keeper/grpc_query_provider_reputation_test.go @@ -0,0 +1,144 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v4/testutil/common" + "github.com/lavanet/lava/v4/x/pairing/types" + "github.com/stretchr/testify/require" +) + +// TestProviderReputation tests the provider-reputation query +func TestProviderReputation(t *testing.T) { + ts := newTester(t) + ts.setupForPayments(4, 0, 0) // 4 providers + + _, p1 := ts.GetAccount(common.PROVIDER, 0) + _, p2 := ts.GetAccount(common.PROVIDER, 1) + _, p3 := ts.GetAccount(common.PROVIDER, 2) + _, p4 := ts.GetAccount(common.PROVIDER, 3) + + specs := []string{"spec1", "spec2", "spec1", "spec1", "spec1", "spec2", "spec2"} + clusters := []string{"cluster1", "cluster1", "cluster2", "cluster1", "cluster1", "cluster1", "cluster2"} + providers := []string{p1, p1, p1, p2, p3, p3, p4} + + // Reputation score setup: + // spec1 + cluster1: p1=1, p2=4, p3=5 + // spec1 + cluster2: p1=3 + // spec2 + cluster1: p1=2, p3=6 + // spec2 + cluster2: p4=7 + for i := range providers { + err := ts.Keepers.Pairing.SetReputationScore(ts.Ctx, specs[i], clusters[i], providers[i], sdk.NewDec(int64(i+1))) + require.NoError(t, err) + } + + // test only on p1 + tests := []struct { + name string + chain string + cluster string + expected []types.ReputationData + }{ + { + "spec1+cluster1", "spec1", "cluster1", []types.ReputationData{ + {Rank: 3, Providers: 3, OverallPerformance: "bad", ChainID: "spec1", Cluster: "cluster1"}, + }, + }, + { + "spec1", "spec1", "*", []types.ReputationData{ + {Rank: 3, Providers: 3, OverallPerformance: "bad", ChainID: "spec1", Cluster: "cluster1"}, + {Rank: 1, Providers: 1, OverallPerformance: "low_variance", ChainID: "spec1", Cluster: "cluster2"}, + }, + }, + { + "cluster1", "*", "cluster1", []types.ReputationData{ + {Rank: 3, Providers: 3, OverallPerformance: "bad", ChainID: "spec1", Cluster: "cluster1"}, + {Rank: 2, Providers: 2, OverallPerformance: "bad", ChainID: "spec2", Cluster: "cluster2"}, + }, + }, + { + "all", "*", "*", []types.ReputationData{ + {Rank: 3, Providers: 3, OverallPerformance: "bad", ChainID: "spec1", Cluster: "cluster1"}, + {Rank: 2, Providers: 2, OverallPerformance: "bad", ChainID: "spec2", Cluster: "cluster2"}, + {Rank: 1, Providers: 1, OverallPerformance: "low_variance", ChainID: "spec1", Cluster: "cluster2"}, + }, + }, + { + "spec2+cluster2 (p1 not exist)", "spec2", "cluster2", []types.ReputationData{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res, err := ts.QueryPairingProviderReputation(p1, tt.chain, tt.cluster) + require.NoError(t, err) + for _, data := range res.Data { + foundChainCluster := false + for _, expected := range tt.expected { + if data.ChainID == expected.ChainID && data.Cluster == expected.Cluster { + foundChainCluster = true + require.Equal(t, expected.Rank, data.Rank) + require.Equal(t, expected.Providers, data.Providers) + require.Equal(t, expected.OverallPerformance, data.OverallPerformance) + } + } + if !foundChainCluster { + require.FailNow(t, "could not find chain cluster pair on-chain") + } + } + }) + } +} + +// TestProviderReputationDetails tests the provider-reputation-details query +func TestProviderReputationDetails(t *testing.T) { + ts := newTester(t) + ts.setupForPayments(2, 0, 0) // 2 providers + + _, p1 := ts.GetAccount(common.PROVIDER, 0) + _, p2 := ts.GetAccount(common.PROVIDER, 1) + + specs := []string{"spec1", "spec2", "spec1", "spec1"} + clusters := []string{"cluster1", "cluster1", "cluster2", "cluster1"} + providers := []string{p1, p1, p1, p2} + + for i := range providers { + ts.Keepers.Pairing.SetReputation(ts.Ctx, specs[i], clusters[i], providers[i], types.Reputation{ + Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(int64(i+1))), + }) + err := ts.Keepers.Pairing.SetReputationScore(ts.Ctx, specs[i], clusters[i], providers[i], sdk.NewDec(int64(i+1))) + require.NoError(t, err) + } + + tests := []struct { + name string + provider string + chain string + cluster string + expected []math.LegacyDec + }{ + {"provider+chain+cluster", p1, "spec1", "cluster1", []math.LegacyDec{math.LegacyNewDec(1)}}, + {"provider+chain+all_clusters", p1, "spec1", "*", []math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(3)}}, + {"provider+all_chain+cluster", p1, "*", "cluster1", []math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(2)}}, + {"provider+all_chains+all_clusters", p1, "*", "*", []math.LegacyDec{math.LegacyNewDec(1), math.LegacyNewDec(2), math.LegacyNewDec(3)}}, + {"second provider+chain+cluster", p2, "spec1", "cluster1", []math.LegacyDec{math.LegacyNewDec(4)}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res, err := ts.QueryPairingProviderReputationDetails(tt.provider, tt.chain, tt.cluster) + require.NoError(t, err) + for i := range res.Data { + expectedStake := sdk.NewCoin(ts.TokenDenom(), tt.expected[i].TruncateInt()) + expectedScore := tt.expected[i] + + require.Equal(t, tt.chain, res.Data[i].ChainID) + require.Equal(t, tt.cluster, res.Data[i].Cluster) + require.True(t, expectedStake.IsEqual(res.Data[i].Reputation.Stake)) + require.True(t, expectedScore.Equal(res.Data[i].ReputationPairingScore.Score)) + } + }) + } +} diff --git a/x/pairing/keeper/helpers_test.go b/x/pairing/keeper/helpers_test.go index f45473d574..64c1fd8f59 100644 --- a/x/pairing/keeper/helpers_test.go +++ b/x/pairing/keeper/helpers_test.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v4/testutil/common" testutil "github.com/lavanet/lava/v4/testutil/keeper" + dualstakingtypes "github.com/lavanet/lava/v4/x/dualstaking/types" epochstoragetypes "github.com/lavanet/lava/v4/x/epochstorage/types" pairingtypes "github.com/lavanet/lava/v4/x/pairing/types" planstypes "github.com/lavanet/lava/v4/x/plans/types" @@ -152,6 +153,36 @@ func (ts *tester) setupForPayments(providersCount, clientsCount, providersToPair return ts } +const ( + GreatQos = iota + GoodQos + BadQos +) + +func (ts *tester) setupForReputation(modifyHalfLifeFactor bool) (*tester, []pairingtypes.QualityOfServiceReport) { + ts.setupForPayments(0, 1, 5) // 0 providers, 1 client, default providers-to-pair + + greatQos := pairingtypes.QualityOfServiceReport{Latency: sdk.OneDec(), Availability: sdk.OneDec(), Sync: sdk.OneDec()} + goodQos := pairingtypes.QualityOfServiceReport{Latency: sdk.NewDec(3), Availability: sdk.OneDec(), Sync: sdk.NewDec(3)} + badQos := pairingtypes.QualityOfServiceReport{Latency: sdk.NewDec(1000), Availability: sdk.OneDec(), Sync: sdk.NewDec(1000)} + + if modifyHalfLifeFactor { + // set half life factor to be epoch time + resQParams, err := ts.Keepers.Pairing.Params(ts.GoCtx, &pairingtypes.QueryParamsRequest{}) + require.NoError(ts.T, err) + resQParams.Params.ReputationHalfLifeFactor = uint64(ts.EpochTimeDefault().Seconds()) + ts.Keepers.Pairing.SetParams(ts.Ctx, resQParams.Params) + } + + // set min self delegation to zero + resQParams2, err := ts.Keepers.Dualstaking.Params(ts.GoCtx, &dualstakingtypes.QueryParamsRequest{}) + require.NoError(ts.T, err) + resQParams2.Params.MinSelfDelegation = sdk.NewCoin(ts.TokenDenom(), sdk.ZeroInt()) + ts.Keepers.Dualstaking.SetParams(ts.Ctx, resQParams2.Params) + + return ts, []pairingtypes.QualityOfServiceReport{greatQos, goodQos, badQos} +} + // payAndVerifyBalance performs payment and then verifies the balances // (provider balance should increase and consumer should decrease) // The providerRewardPerc arg is the part of the provider reward after deducting diff --git a/x/pairing/keeper/keeper.go b/x/pairing/keeper/keeper.go index 62050236aa..5f93ceeddf 100644 --- a/x/pairing/keeper/keeper.go +++ b/x/pairing/keeper/keeper.go @@ -3,6 +3,9 @@ package keeper import ( "fmt" + "cosmossdk.io/collections" + collcompat "github.com/lavanet/lava/v4/utils/collcompat" + storetypes "github.com/cosmos/cosmos-sdk/store/types" timerstoretypes "github.com/lavanet/lava/v4/x/timerstore/types" @@ -30,10 +33,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 chain, cluster, provider } ) @@ -71,6 +77,8 @@ func NewKeeper( ps = ps.WithKeyTable(types.ParamKeyTable()) } + sb := collections.NewSchemaBuilder(collcompat.NewKVStoreService(storeKey)) + keeper := &Keeper{ cdc: cdc, storeKey: storeKey, @@ -86,6 +94,11 @@ func NewKeeper( downtimeKeeper: downtimeKeeper, dualstakingKeeper: dualstakingKeeper, stakingKeeper: stakingKeeper, + + reputations: collections.NewMap(sb, types.ReputationPrefix, "reputations", + collections.TripleKeyCodec(collections.StringKey, collections.StringKey, collections.StringKey), + collcompat.ProtoValue[types.Reputation](cdc), + ), } // note that the timer and badgeUsedCu keys are the same (so we can use only the second arg) @@ -96,7 +109,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 } @@ -107,6 +126,8 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) BeginBlock(ctx sdk.Context) { if k.epochStorageKeeper.IsEpochStart(ctx) { + // update reputations by QoS scores + k.UpdateAllReputationQosScore(ctx) // remove old session payments k.RemoveOldEpochPayments(ctx) // unstake/jail unresponsive providers @@ -121,10 +142,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) } diff --git a/x/pairing/keeper/msg_server_relay_payment.go b/x/pairing/keeper/msg_server_relay_payment.go index a1ec9c0f3f..9eb2017373 100644 --- a/x/pairing/keeper/msg_server_relay_payment.go +++ b/x/pairing/keeper/msg_server_relay_payment.go @@ -70,7 +70,7 @@ func (k msgServer) RelayPayment(goCtx context.Context, msg *types.MsgRelayPaymen } var rejectedCu uint64 // aggregated rejected CU (due to badge CU overuse or provider double spending) - rejected_relays_num := len(msg.Relays) + rejectedRelaysNum := len(msg.Relays) for relayIdx, relay := range msg.Relays { rejectedCu += relay.CuSum providerAddr, err := sdk.AccAddressFromBech32(relay.Provider) @@ -170,6 +170,22 @@ func (k msgServer) RelayPayment(goCtx context.Context, msg *types.MsgRelayPaymen k.handleBadgeCu(ctx, badgeData, relay.Provider, relay.CuSum, newBadgeTimerExpiry) } + // update the reputation's epoch QoS score + // the excellece QoS report can be nil when the provider and consumer geolocations are not equal + if relay.QosExcellenceReport != nil { + err = k.aggregateReputationEpochQosScore(ctx, project.Subscription, relay) + if err != nil { + return nil, utils.LavaFormatWarning("RelayPayment: could not update reputation epoch QoS score", err, + utils.LogAttr("consumer", project.Subscription), + utils.LogAttr("project", project.Index), + utils.LogAttr("chain", relay.SpecId), + utils.LogAttr("provider", relay.Provider), + utils.LogAttr("qos_excellence_report", relay.QosExcellenceReport.String()), + utils.LogAttr("sync_factor", k.ReputationLatencyOverSyncFactor(ctx).String()), + ) + } + } + // TODO: add support for spec changes spec, found := k.specKeeper.GetSpec(ctx, relay.SpecId) if !found || !spec.Enabled { @@ -290,11 +306,11 @@ func (k msgServer) RelayPayment(goCtx context.Context, msg *types.MsgRelayPaymen ) } rejectedCu -= relay.CuSum - rejected_relays_num-- + rejectedRelaysNum-- } // if all relays failed, fail the TX - if rejected_relays_num != 0 { + if rejectedRelaysNum != 0 { return nil, utils.LavaFormatWarning("relay payment failed", fmt.Errorf("all relays rejected"), utils.Attribute{Key: "provider", Value: msg.Creator}, utils.Attribute{Key: "description", Value: msg.DescriptionString}, @@ -473,3 +489,40 @@ func (k Keeper) handleBadgeCu(ctx sdk.Context, badgeData BadgeData, provider str badgeUsedCuMapEntry.UsedCu += relayCuSum k.SetBadgeUsedCu(ctx, badgeUsedCuMapEntry) } + +func (k Keeper) aggregateReputationEpochQosScore(ctx sdk.Context, subscription string, relay *types.RelaySession) error { + sub, found := k.subscriptionKeeper.GetSubscription(ctx, subscription) + if !found { + return utils.LavaFormatError("RelayPayment: could not get cluster for reputation score update", fmt.Errorf("relay consumer's subscription not found"), + utils.LogAttr("subscription", subscription), + utils.LogAttr("chain", relay.SpecId), + utils.LogAttr("provider", relay.Provider), + ) + } + + syncFactor := k.ReputationLatencyOverSyncFactor(ctx) + score, err := relay.QosExcellenceReport.ComputeQosExcellenceForReputation(syncFactor) + if err != nil { + return utils.LavaFormatWarning("RelayPayment: could not compute qos excellence score", err, + utils.LogAttr("consumer", subscription), + utils.LogAttr("chain", relay.SpecId), + utils.LogAttr("provider", relay.Provider), + utils.LogAttr("qos_excellence_report", relay.QosExcellenceReport.String()), + utils.LogAttr("sync_factor", syncFactor.String()), + ) + } + + stakeEntry, found := k.epochStorageKeeper.GetStakeEntryCurrent(ctx, relay.SpecId, relay.Provider) + if !found { + return utils.LavaFormatWarning("RelayPayment: could not get stake entry for reputation", fmt.Errorf("stake entry not found"), + utils.LogAttr("consumer", subscription), + utils.LogAttr("chain", relay.SpecId), + utils.LogAttr("provider", relay.Provider), + ) + } + effectiveStake := sdk.NewCoin(stakeEntry.Stake.Denom, stakeEntry.TotalStake()) + + // note the current weight used is by cu. In the future, it might change + k.UpdateReputationEpochQosScore(ctx, relay.SpecId, sub.Cluster, relay.Provider, score, utils.SafeUint64ToInt64Convert(relay.CuSum), effectiveStake) + return nil +} diff --git a/x/pairing/keeper/msg_server_relay_payment_test.go b/x/pairing/keeper/msg_server_relay_payment_test.go index 2103c22bcb..7179fac1bb 100644 --- a/x/pairing/keeper/msg_server_relay_payment_test.go +++ b/x/pairing/keeper/msg_server_relay_payment_test.go @@ -3,11 +3,13 @@ package keeper_test import ( "testing" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v4/testutil/common" commonconsts "github.com/lavanet/lava/v4/testutil/common/consts" "github.com/lavanet/lava/v4/utils/lavaslices" "github.com/lavanet/lava/v4/utils/sigs" + "github.com/lavanet/lava/v4/x/pairing/keeper" "github.com/lavanet/lava/v4/x/pairing/types" planstypes "github.com/lavanet/lava/v4/x/plans/types" projectstypes "github.com/lavanet/lava/v4/x/projects/types" @@ -1063,3 +1065,788 @@ func TestPairingCaching(t *testing.T) { require.Equal(t, totalCU*3, sub.Sub.MonthCuTotal-sub.Sub.MonthCuLeft) } } + +// TestUpdateReputationEpochQosScore tests the update of the reputation's epoch qos score +// Scenarios: +// 1. provider1 sends relay -> its reputation is updated (epoch score and time last updated), +// also, provider2 reputation is not updated +func TestUpdateReputationEpochQosScore(t *testing.T) { + ts := newTester(t) + ts.setupForPayments(2, 1, 0) // 2 providers, 1 client, default providers-to-pair + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + _, provider1 := ts.GetAccount(common.PROVIDER, 0) + _, provider2 := ts.GetAccount(common.PROVIDER, 1) + qos := &types.QualityOfServiceReport{ + Latency: sdk.OneDec(), + Availability: sdk.NewDecWithPrec(1, 1), + Sync: sdk.OneDec(), + } + + res, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster := res.Sub.Cluster + + // set default reputations for both providers. Advance epoch to change the current block time + ts.Keepers.Pairing.SetReputation(ts.Ctx, ts.spec.Index, cluster, provider1, types.NewReputation(ts.Ctx)) + ts.Keepers.Pairing.SetReputation(ts.Ctx, ts.spec.Index, cluster, provider2, types.NewReputation(ts.Ctx)) + ts.AdvanceEpoch() + + // send relay payment msg from provider1 + relaySession := ts.newRelaySession(provider1, 0, 100, ts.BlockHeight(), 1) + relaySession.QosExcellenceReport = qos + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: provider1, + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + + // get both providers reputation: provider1 should have its epoch score changed, + // provider2 should have nothing change from the default + r1, found := ts.Keepers.Pairing.GetReputation(ts.Ctx, ts.spec.Index, cluster, provider1) + require.True(t, found) + r2, found := ts.Keepers.Pairing.GetReputation(ts.Ctx, ts.spec.Index, cluster, provider2) + require.True(t, found) + + require.Equal(t, r1.TimeLastUpdated, r2.TimeLastUpdated) + epochScore1, err := r1.EpochScore.Score.Resolve() + require.NoError(t, err) + epochScore2, err := r2.EpochScore.Score.Resolve() + require.NoError(t, err) + variance1, err := r1.EpochScore.Variance.Resolve() + require.NoError(t, err) + variance2, err := r2.EpochScore.Variance.Resolve() + require.NoError(t, err) + require.True(t, epochScore1.GT(epochScore2)) // score is higher because QoS is bad + require.True(t, variance1.GT(variance2)) // variance is higher because the QoS is significantly differnet from DefaultQos + + entry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider1) + require.True(t, found) + require.True(t, entry.Stake.IsEqual(r1.Stake)) +} + +// TestUpdateReputationEpochQosScoreTruncation tests the following scenarios: +// 1. stabilization period has not passed -> no truncation +// 2. stabilization period passed -> with truncation (score update is smaller than the first one) +// note, this test works since we use a bad QoS report (compared to default) so we know that the score should +// increase (which is considered worse) +func TestUpdateReputationEpochQosScoreTruncation(t *testing.T) { + // these will be used to compare the score change with/without truncation + scoreUpdates := []sdk.Dec{} + + // we set the stabilization period to 2 epochs time. Advancing one epoch means we won't truncate, + // advancing 3 means we will truncate. + epochsToAdvance := []uint64{1, 3} + + for i := range epochsToAdvance { + ts := newTester(t) + ts.setupForPayments(1, 1, 0) // 1 provider, 1 client, default providers-to-pair + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + _, provider1 := ts.GetAccount(common.PROVIDER, 0) + qos := &types.QualityOfServiceReport{ + Latency: sdk.NewDec(1000), + Availability: sdk.OneDec(), + Sync: sdk.NewDec(1000), + } + + resQCurrent, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster := resQCurrent.Sub.Cluster + + // set stabilization period to be 2*epoch time + resQParams, err := ts.Keepers.Pairing.Params(ts.GoCtx, &types.QueryParamsRequest{}) + require.NoError(t, err) + resQParams.Params.ReputationVarianceStabilizationPeriod = int64(ts.EpochTimeDefault().Seconds()) + ts.Keepers.Pairing.SetParams(ts.Ctx, resQParams.Params) + + // set default reputation + ts.Keepers.Pairing.SetReputation(ts.Ctx, ts.spec.Index, cluster, provider1, types.NewReputation(ts.Ctx)) + + // advance epochs + ts.AdvanceEpochs(epochsToAdvance[i]) + + // send relay payment msg from provider1 + relaySession := ts.newRelaySession(provider1, 0, 100, ts.BlockHeight(), 1) + relaySession.QosExcellenceReport = qos + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: provider1, + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + + // get update of epoch score + r, found := ts.Keepers.Pairing.GetReputation(ts.Ctx, ts.spec.Index, cluster, provider1) + require.True(t, found) + epochScoreNoTruncation, err := r.EpochScore.Score.Resolve() + require.NoError(t, err) + defaultEpochScore, err := types.ZeroQosScore.Score.Resolve() + require.NoError(t, err) + scoreUpdates = append(scoreUpdates, epochScoreNoTruncation.Sub(defaultEpochScore)) + } + + // require that the score update that was not truncated is larger than the one that was truncated + require.True(t, scoreUpdates[0].GT(scoreUpdates[1])) +} + +// TestUpdateReputationEpochQosScoreTruncation tests the following scenario: +// 1. relay num is the reputation update weight. More relays = bigger update +func TestUpdateReputationEpochQosScoreRelayNumWeight(t *testing.T) { + // these will be used to compare the score change with high/low relay numbers + scoreUpdates := []sdk.Dec{} + + // we set the number of clients to be 1 and 2 to test different relay amount in + // a session + clientsAmount := []int{2, 1} + + for i := range clientsAmount { + ts := newTester(t) + ts.setupForPayments(1, clientsAmount[i], 0) // 1 provider, clientsAmount[i] clients, default providers-to-pair + _, provider1 := ts.GetAccount(common.PROVIDER, 0) + relays := []*types.RelaySession{} + cluster := "" + for j := 0; j < clientsAmount[i]; j++ { + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, j) + + qos := &types.QualityOfServiceReport{ + Latency: sdk.NewDec(1000), + Availability: sdk.OneDec(), + Sync: sdk.NewDec(1000), + } + + resQCurrent, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster = resQCurrent.Sub.Cluster + + // set stabilization period to be 2*epoch time to avoid truncation + resQParams, err := ts.Keepers.Pairing.Params(ts.GoCtx, &types.QueryParamsRequest{}) + require.NoError(t, err) + resQParams.Params.ReputationVarianceStabilizationPeriod = int64(ts.EpochTimeDefault().Seconds()) + ts.Keepers.Pairing.SetParams(ts.Ctx, resQParams.Params) + + // set default reputation + ts.Keepers.Pairing.SetReputation(ts.Ctx, ts.spec.Index, cluster, provider1, types.NewReputation(ts.Ctx)) + ts.AdvanceEpoch() + + // send relay payment msg from provider1 + relaySession := ts.newRelaySession(provider1, 0, 100, ts.BlockHeight(), uint64(j)) + relaySession.QosExcellenceReport = qos + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + relays = append(relays, relaySession) + } + payment := types.MsgRelayPayment{ + Creator: provider1, + Relays: relays, + } + ts.relayPaymentWithoutPay(payment, true) + + // get update of epoch score + r, found := ts.Keepers.Pairing.GetReputation(ts.Ctx, ts.spec.Index, cluster, provider1) + require.True(t, found) + epochScoreNoTruncation, err := r.EpochScore.Score.Resolve() + require.NoError(t, err) + defaultEpochScore, err := types.ZeroQosScore.Score.Resolve() + require.NoError(t, err) + scoreUpdates = append(scoreUpdates, epochScoreNoTruncation.Sub(defaultEpochScore)) + } + + // require that the score update that was with 1000 relay num is larger than the one with one relay num + require.True(t, scoreUpdates[0].GT(scoreUpdates[1])) +} + +// TestUpdateReputationScores tests that on epoch start: score is modified, epoch score is zeroed time last +// updated is modified, and reputation stake changed to current stake. We do this with the following scenario: +// 1. we set half life factor to be one epoch time +// 2. we set a provider with default QoS score, let him send a relay and advance epoch +// 3. we check expected result +func TestReputationUpdateOnEpochStart(t *testing.T) { + ts := newTester(t) + ts.setupForPayments(1, 1, 0) // 1 provider, 1 client, default providers-to-pair + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + _, provider1 := ts.GetAccount(common.PROVIDER, 0) + qos := &types.QualityOfServiceReport{ + Latency: sdk.NewDec(1000), + Availability: sdk.OneDec(), + Sync: sdk.NewDec(1000), + } + + resQCurrent, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster := resQCurrent.Sub.Cluster + + // set half life factor to be epoch time + resQParams, err := ts.Keepers.Pairing.Params(ts.GoCtx, &types.QueryParamsRequest{}) + require.NoError(t, err) + resQParams.Params.ReputationHalfLifeFactor = uint64(ts.EpochTimeDefault().Seconds()) + ts.Keepers.Pairing.SetParams(ts.Ctx, resQParams.Params) + + // set default reputation and keep some properties for future comparison + ts.Keepers.Pairing.SetReputation(ts.Ctx, ts.spec.Index, cluster, provider1, types.NewReputation(ts.Ctx)) + creationTime := ts.BlockTime().UTC().Unix() + entry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider1) + require.True(t, found) + stake := sdk.NewCoin(entry.Stake.Denom, entry.TotalStake().AddRaw(1)) + + // stake some more coins and advance epoch + err = ts.StakeProvider(entry.Vault, entry.Address, ts.spec, entry.TotalStake().Int64()) + require.NoError(t, err) + ts.AdvanceEpoch() + + // save reputation original time and advance hour + r, found := ts.Keepers.Pairing.GetReputation(ts.Ctx, ts.spec.Index, cluster, provider1) + require.True(t, found) + originalTime := r.TimeLastUpdated + ts.AdvanceTimeHours(1) + + // send relay payment msg from provider1 + relaySession := ts.newRelaySession(provider1, 0, 100, ts.BlockHeight(), 10) + relaySession.QosExcellenceReport = qos + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: provider1, + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + + // check that the time last updated is the same as the original time + // (a bug in which the TimeLastUpdated was updated in relay payment was fixed. + // The TimeLastUpdated should be updated only when the epoch starts) + require.Equal(t, originalTime, r.TimeLastUpdated) + + // advance epoch and check reputation for expected results + ts.AdvanceEpoch() + reputation, found := ts.Keepers.Pairing.GetReputation(ts.Ctx, ts.spec.Index, cluster, provider1) + require.True(t, found) + require.False(t, reputation.Score.Equal(types.ZeroQosScore)) + require.True(t, reputation.EpochScore.Equal(types.ZeroQosScore)) + require.Equal(t, ts.BlockTime().UTC().Unix(), reputation.TimeLastUpdated) + require.Equal(t, creationTime, reputation.CreationTime) + require.NotEqual(t, reputation.CreationTime, reputation.TimeLastUpdated) + + entry, found = ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider1) + require.True(t, found) + stakeAfterUpdate := sdk.NewCoin(entry.Stake.Denom, entry.TotalStake()) + + require.False(t, stakeAfterUpdate.IsEqual(stake)) + require.True(t, reputation.Stake.IsEqual(stakeAfterUpdate)) +} + +// TestUpdateReputationScoresMap checks that after calling UpdateReputationsForEpochStart() we get a sorted (by QoS score) +// map[chain+cluster]stakeProviderScores in ascending order. We do this with the following scenario: +// 1. have 4 providers: 2 on chain "mockspec", 3 on chain "mockspec1" (one provider is staked on both) +// 2. let the providers have different reputation scores and different stakes and call updateReputationsScores() +// 3. expect to see two map keys (for the two chains) and a list of providers in ascending order by QoS score +func TestUpdateReputationScoresSortedMap(t *testing.T) { + ts := newTester(t) + + // create provider addresses + _, p1 := ts.AddAccount(common.PROVIDER, 0, testStake) + _, p2 := ts.AddAccount(common.PROVIDER, 1, testStake) + _, p3 := ts.AddAccount(common.PROVIDER, 2, testStake) + _, p4 := ts.AddAccount(common.PROVIDER, 3, testStake) + + // set reputations like described above + providers := []string{p1, p2, p1, p3, p4} // p1 will be "staked" on two chains + specs := []string{"mockspec", "mockspec", "mockspec1", "mockspec1", "mockspec1"} + stakes := []math.Int{sdk.NewInt(1), sdk.NewInt(2), sdk.NewInt(3), sdk.NewInt(4), sdk.NewInt(5)} + zeroFrac := types.Frac{Num: sdk.ZeroDec(), Denom: sdk.MaxSortableDec} + epochScores := []types.QosScore{ + {Score: types.Frac{Num: sdk.NewDec(6), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, // score = 2 + {Score: types.Frac{Num: sdk.NewDec(9), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, // score = 3 + {Score: types.Frac{Num: sdk.NewDec(3), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, // score = 1 + {Score: types.Frac{Num: sdk.NewDec(12), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, // score = 4 + {Score: types.Frac{Num: sdk.NewDec(9), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, // score = 3 + } + for i := range providers { + reputation := types.NewReputation(ts.Ctx) + reputation.EpochScore = epochScores[i] + reputation.Score = types.ZeroQosScore + reputation.Stake = sdk.NewCoin(ts.TokenDenom(), stakes[i]) + ts.Keepers.Pairing.SetReputation(ts.Ctx, specs[i], "cluster", providers[i], reputation) + } + + // create expected map (supposed to be sorted by score) + expected := map[types.ReputationChainClusterKey]keeper.StakeProviderScores{ + {ChainID: "mockspec", Cluster: "cluster"}: { + TotalStake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(3)), // 1+2 + ProviderScores: []keeper.ProviderQosScore{ + { + Provider: p1, + Score: types.QosScore{Score: types.Frac{Num: sdk.NewDec(6), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, + Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(1)), + }, + { + Provider: p2, + Score: types.QosScore{Score: types.Frac{Num: sdk.NewDec(9), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, + Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(2)), + }, + }, + }, + + {ChainID: "mockspec1", Cluster: "cluster"}: { + TotalStake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(12)), // 3+4+5 + ProviderScores: []keeper.ProviderQosScore{ + { + Provider: p1, + Score: types.QosScore{Score: types.Frac{Num: sdk.NewDec(3), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, + Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(3)), + }, + { + Provider: p4, + Score: types.QosScore{Score: types.Frac{Num: sdk.NewDec(9), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, + Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(5)), + }, + { + Provider: p3, + Score: types.QosScore{Score: types.Frac{Num: sdk.NewDec(12), Denom: sdk.NewDec(3)}, Variance: zeroFrac}, + Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(4)), + }, + }, + }, + } + + // call UpdateReputationsForEpochStart() and check the scores map + scores, err := ts.Keepers.Pairing.UpdateReputationsForEpochStart(ts.Ctx) + require.NoError(t, err) + + for chainCluster, stakeProviderScores := range scores { + expectedScores := expected[chainCluster] + require.True(t, expectedScores.TotalStake.IsEqual(stakeProviderScores.TotalStake)) + for i := range stakeProviderScores.ProviderScores { + require.Equal(t, expectedScores.ProviderScores[i].Provider, stakeProviderScores.ProviderScores[i].Provider) + require.True(t, expectedScores.ProviderScores[i].Stake.IsEqual(stakeProviderScores.ProviderScores[i].Stake)) + + expectedScore, err := expectedScores.ProviderScores[i].Score.Score.Resolve() + require.NoError(t, err) + score, err := stakeProviderScores.ProviderScores[i].Score.Score.Resolve() + require.NoError(t, err) + require.True(t, expectedScore.RoundInt().Equal(score.RoundInt())) + } + } +} + +// TestReputationPairingScoreBenchmark tests that the benchmark value after calling getBenchmarkReputationScore() +// is the score of the last provider we iterate on when aggregating stake to pass ReputationPairingScoreBenchmarkStakeThreshold. +// +// We do this with the following scenario: +// 1. have 5 providers with varying stakes -> providers with 10% of stake should have max scores, the others should have +// pairing scores that differ by their QoS scores difference +func TestReputationPairingScoreBenchmark(t *testing.T) { + ts := newTester(t) + totalStake := sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(1000)) // threshold for benchmark will be 100ulava + + // create provider addresses + _, p1 := ts.AddAccount(common.PROVIDER, 0, testStake) + _, p2 := ts.AddAccount(common.PROVIDER, 1, testStake) + _, p3 := ts.AddAccount(common.PROVIDER, 2, testStake) + _, p4 := ts.AddAccount(common.PROVIDER, 3, testStake) + _, p5 := ts.AddAccount(common.PROVIDER, 4, testStake) + + qosScores := createQosScoresForBenchmarkTest() + + // create a StakeProviderScores object with ProviderScores list which is descending by score + sps := keeper.StakeProviderScores{ + TotalStake: totalStake, + ProviderScores: []keeper.ProviderQosScore{ + {Provider: p1, Score: qosScores[0], Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(1))}, + {Provider: p2, Score: qosScores[1], Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(19))}, + {Provider: p3, Score: qosScores[2], Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(40))}, + {Provider: p4, Score: qosScores[3], Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(60))}, + {Provider: p5, Score: qosScores[4], Stake: sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(1000))}, + }, + } + + // the benchmark should be equal to qosScores[3] since p4 is the last provider on which the + // stake aggregation should complete (1+19+40+60=100) + benchmark, err := ts.Keepers.Pairing.GetBenchmarkReputationScore(sps) + require.NoError(t, err) + expectedBenchmark, err := qosScores[3].Score.Resolve() + require.NoError(t, err) + require.True(t, benchmark.Equal(expectedBenchmark)) +} + +// createQosScoresForBenchmarkTest is a helper function to create an array of QoS scores +func createQosScoresForBenchmarkTest() []types.QosScore { + nums := []math.LegacyDec{sdk.NewDec(1000), sdk.NewDec(100), sdk.NewDec(90), sdk.NewDec(40), sdk.NewDec(10)} + zeroFrac := types.Frac{Num: sdk.ZeroDec(), Denom: sdk.MaxSortableDec} + qosScores := []types.QosScore{} + for _, num := range nums { + scoreFrac, err := types.NewFrac(num, sdk.OneDec()) + if err != nil { + panic(err) + } + qs := types.NewQosScore(scoreFrac, zeroFrac) + qosScores = append(qosScores, qs) + } + return qosScores +} + +// TestReputationPairingScore tests that on epoch start: +// 1. The reputation pairing score is set. +// 2. Providers that didn't send relays get the default reputation pairing score. +// 2. If a provider has a bad QoS report, it yields a bad reputation pairing score. +// We test it by having 4 providers: +// +// p1: high stake and great QoS (will be used as benchmark), +// p2: low stake and good QoS score +// p3: same low stake and bad QoS score +// p4: high stake that doesn't send relays. +// +// We check that all 4 have reputation pairing score: p1 with max score, p2 with better score than p3, p4 +// has no reputation pairing score. +func TestReputationPairingScore(t *testing.T) { + ts := newTester(t) + ts, reports := ts.setupForReputation(false) + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + resQCurrent, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster := resQCurrent.Sub.Cluster + + minStake := ts.spec.MinStakeProvider.Amount.Int64() + stakes := []int64{minStake * 5, minStake + 10, minStake + 10, minStake * 2} + providers := []string{} + for i := 0; i < 4; i++ { + _, provider := ts.AddAccount(common.PROVIDER, i, testBalance) + providers = append(providers, provider) + } + for i := 0; i < 4; i++ { + // create providers + err = ts.StakeProvider(providers[i], providers[i], ts.spec, stakes[i]) + require.NoError(t, err) + } + // advance epoch to apply pairing + ts.AdvanceEpoch() + + // send relays (except for last one) + for i := 0; i < 3; i++ { + relaySession := ts.newRelaySession(providers[i], 0, 100, ts.BlockHeight(), 10) + relaySession.QosExcellenceReport = &reports[i] + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: providers[i], + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + } + + // advance epoch to update pairing scores + ts.AdvanceEpoch() + + // check results + pairingScores := []math.LegacyDec{} + for i := range providers { + score, found := ts.Keepers.Pairing.GetReputationScore(ts.Ctx, ts.spec.Index, cluster, providers[i]) + if i < 3 { + require.True(t, found) + pairingScores = append(pairingScores, score) + } else { + require.False(t, found) + } + } + require.True(t, pairingScores[0].Equal(types.MaxReputationPairingScore)) + require.True(t, pairingScores[1].GT(pairingScores[2])) +} + +// TestReputationPairingScoreWithinRange tests that the reputation pairing score is always between +// MinReputationPairingScore and MaxReputationPairingScore. +// We test it by setting two providers with extreme QoS reports: one very good, and one very bad. +// We expect that both pairing scores will be in the expected range. +func TestReputationPairingScoreWithinRange(t *testing.T) { + ts := newTester(t) + ts, reports := ts.setupForReputation(false) + + greatQos := reports[GreatQos] + badQos := reports[BadQos] + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + resQCurrent, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster := resQCurrent.Sub.Cluster + + providers := []string{} + for i := 0; i < 2; i++ { + _, provider := ts.AddAccount(common.PROVIDER, i, testBalance) + providers = append(providers, provider) + } + + minStake := ts.spec.MinStakeProvider.Amount.Int64() + for i := 0; i < 2; i++ { + // create providers + err = ts.StakeProvider(providers[i], providers[i], ts.spec, minStake) + require.NoError(t, err) + } + // advance epoch to apply pairing + ts.AdvanceEpoch() + + // send relays + for i := 0; i < 2; i++ { + relaySession := ts.newRelaySession(providers[i], 0, 100, ts.BlockHeight(), 10) + report := greatQos + if i == 0 { + report = badQos + } + relaySession.QosExcellenceReport = &report + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: providers[i], + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + } + + // advance epoch to update pairing scores + ts.AdvanceEpoch() + + // check results are within the expected range + for i := range providers { + score, found := ts.Keepers.Pairing.GetReputationScore(ts.Ctx, ts.spec.Index, cluster, providers[i]) + require.True(t, found) + if score.LT(types.MinReputationPairingScore) || score.GT(types.MaxReputationPairingScore) { + require.FailNow(t, "score is not within expected pairing score range") + } + } +} + +// TestReputationPairingScoreZeroQosScores tests that if all providers have a QoS score of zero, +// they all get the max reputation pairing score. +// We test it by having two providers with zero QoS score. We expect that both will have max reputation +// pairing score. +func TestReputationPairingScoreZeroQosScores(t *testing.T) { + ts := newTester(t) + ts, _ = ts.setupForReputation(false) + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + resQCurrent, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster := resQCurrent.Sub.Cluster + + providers := []string{} + for i := 0; i < 2; i++ { + _, provider := ts.AddAccount(common.PROVIDER, i, testBalance) + providers = append(providers, provider) + } + + minStake := ts.spec.MinStakeProvider.Amount.Int64() + for i := 0; i < 2; i++ { + // create providers + err = ts.StakeProvider(providers[i], providers[i], ts.spec, minStake) + require.NoError(t, err) + } + // advance epoch to apply pairing + ts.AdvanceEpoch() + + // send relays + for i := 0; i < 2; i++ { + relaySession := ts.newRelaySession(providers[i], 0, 100, ts.BlockHeight(), 10) + perfectQosReport := types.QualityOfServiceReport{ + Latency: sdk.ZeroDec(), + Availability: sdk.OneDec(), + Sync: sdk.ZeroDec(), + } + relaySession.QosExcellenceReport = &perfectQosReport + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: providers[i], + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + } + + // advance epoch to update pairing scores + ts.AdvanceEpoch() + + // check results are max pairing score + for i := range providers { + score, found := ts.Keepers.Pairing.GetReputationScore(ts.Ctx, ts.spec.Index, cluster, providers[i]) + require.True(t, found) + require.True(t, score.Equal(types.MaxReputationPairingScore)) + } +} + +// TestReputationPairingScoreFixation tests that the reputation pairing score is saved in a fixation store +// and can be fetched using a block argument. +// We test this by making two providers, one with high stake and great QoS score and one with low stake and +// bad QoS score. We get the bad provider pairing score and send another relay with a slightly improved QoS +// score. We expect that fetching the pairing score from the past will be lower than the current one +func TestReputationPairingScoreFixation(t *testing.T) { + ts := newTester(t) + ts, reports := ts.setupForReputation(false) + + greatQos := reports[GreatQos] + goodQos := reports[GoodQos] + badQos := reports[BadQos] + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + resQCurrent, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster := resQCurrent.Sub.Cluster + + providers := []string{} + for i := 0; i < 2; i++ { + _, provider := ts.AddAccount(common.PROVIDER, i, testBalance) + providers = append(providers, provider) + } + + minStake := ts.spec.MinStakeProvider.Amount.Int64() + for i := 0; i < 2; i++ { + // create providers + err = ts.StakeProvider(providers[i], providers[i], ts.spec, minStake) + require.NoError(t, err) + } + // advance epoch to apply pairing + ts.AdvanceEpoch() + + // send relays + for i := 0; i < 2; i++ { + relaySession := ts.newRelaySession(providers[i], 0, 100, ts.BlockHeight(), 10) + + if i == 0 { + relaySession.QosExcellenceReport = &greatQos + } else { + relaySession.QosExcellenceReport = &badQos + } + + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: providers[i], + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + } + + // advance epoch to update pairing scores + ts.AdvanceEpoch() + badQosBlock := ts.BlockHeight() + + // send another relay with provider1 with a better QoS report + relaySession := ts.newRelaySession(providers[1], 0, 100, ts.BlockHeight(), 10) + relaySession.QosExcellenceReport = &goodQos + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: providers[1], + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + + // advance epoch to update pairing scores + ts.AdvanceEpoch() + goodQosBlock := ts.BlockHeight() + + // fetch pairing score for both badQosBlock and goodQosBlock + // verify that the pairing score changes for the better + badQosPairingScore, _, found := ts.Keepers.Pairing.GetReputationScoreForBlock(ts.Ctx, ts.spec.Index, cluster, providers[1], badQosBlock) + require.True(t, found) + goodQosPairingScore, _, found := ts.Keepers.Pairing.GetReputationScoreForBlock(ts.Ctx, ts.spec.Index, cluster, providers[1], goodQosBlock) + require.True(t, found) + require.True(t, goodQosPairingScore.GT(badQosPairingScore)) +} + +// TestReputationPairingScoreStakeAggregation tests that the benchmark is determined by stake. We test the following +// scenarios: +// 1. Have 2 providers with equal stake -> pairing scores should differ by their QoS scores difference +// 2. Have 2 providers one with high score and low stake, the other with low score and high stake -> both should get max +// pairing score +func TestReputationPairingScoreStakeAggregation(t *testing.T) { + ts := newTester(t) + ts, reports := ts.setupForReputation(false) + + greatQos := reports[GreatQos] + goodQos := reports[GoodQos] + badQos := reports[BadQos] + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + resQCurrent, err := ts.QuerySubscriptionCurrent(consumer) + require.NoError(t, err) + cluster := resQCurrent.Sub.Cluster + + providers := []string{} + for i := 0; i < 2; i++ { + _, provider := ts.AddAccount(common.PROVIDER, i, testBalance) + providers = append(providers, provider) + } + minStake := ts.spec.MinStakeProvider.Amount.Int64() + + tests := []struct { + name string + stakes []int64 + reports []types.QualityOfServiceReport + differentScores bool + }{ + {"equal stake, different QoS", []int64{minStake, minStake}, []types.QualityOfServiceReport{goodQos, badQos}, true}, + {"high score + low stake, low score + high stake", []int64{minStake, minStake * 20}, []types.QualityOfServiceReport{greatQos, badQos}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for i := 0; i < 2; i++ { + // create providers + err = ts.StakeProvider(providers[i], providers[i], ts.spec, tt.stakes[i]) + require.NoError(t, err) + } + // advance epoch to apply pairing + ts.AdvanceEpoch() + + // send relays + for i := 0; i < 2; i++ { + relaySession := ts.newRelaySession(providers[i], 0, 100, ts.BlockHeight(), 10) + relaySession.QosExcellenceReport = &tt.reports[i] + sig, err := sigs.Sign(consumerAcc.SK, *relaySession) + require.NoError(t, err) + relaySession.Sig = sig + + payment := types.MsgRelayPayment{ + Creator: providers[i], + Relays: []*types.RelaySession{relaySession}, + } + ts.relayPaymentWithoutPay(payment, true) + } + + // advance epoch to update pairing scores + ts.AdvanceEpoch() + + // get pairing scores and check results + pairingScores := []math.LegacyDec{} + for i := range providers { + score, found := ts.Keepers.Pairing.GetReputationScore(ts.Ctx, ts.spec.Index, cluster, providers[i]) + require.True(t, found) + pairingScores = append(pairingScores, score) + } + + if tt.differentScores { + require.True(t, pairingScores[0].GT(pairingScores[1])) + } else { + require.True(t, pairingScores[0].Equal(pairingScores[1])) + require.True(t, pairingScores[0].Equal(types.MaxReputationPairingScore)) + } + }) + } +} diff --git a/x/pairing/keeper/pairing_test.go b/x/pairing/keeper/pairing_test.go index 2972491723..b0941d8d80 100644 --- a/x/pairing/keeper/pairing_test.go +++ b/x/pairing/keeper/pairing_test.go @@ -1131,14 +1131,8 @@ func TestGeolocationPairingScores(t *testing.T) { stakeEntries := providersRes.StakeEntry providerScores := []*pairingscores.PairingScore{} - subRes, err := ts.QuerySubscriptionCurrent(tt.dev.Addr.String()) - require.NoError(t, err) - cluster := subRes.Sub.Cluster - for i := range stakeEntries { - // TODO: require err to be nil once the providerQosFS's update is implemented - qos, _ := ts.Keepers.Pairing.GetQos(ts.Ctx, ts.spec.Index, cluster, stakeEntries[i].Address) - providerScore := pairingscores.NewPairingScore(&stakeEntries[i], qos) + providerScore := pairingscores.NewPairingScore(&stakeEntries[i], sdk.OneDec()) providerScores = append(providerScores, providerScore) } diff --git a/x/pairing/keeper/params.go b/x/pairing/keeper/params.go index 38914328aa..9614a6c349 100644 --- a/x/pairing/keeper/params.go +++ b/x/pairing/keeper/params.go @@ -71,7 +71,7 @@ func (k Keeper) ReputationLatencyOverSyncFactor(ctx sdk.Context) (res math.Legac } // ReputationHalfLifeFactor returns the ReputationHalfLifeFactor param -func (k Keeper) ReputationHalfLifeFactor(ctx sdk.Context) (res int64) { +func (k Keeper) ReputationHalfLifeFactor(ctx sdk.Context) (res uint64) { k.paramstore.Get(ctx, types.KeyReputationHalfLifeFactor, &res) return } diff --git a/x/pairing/keeper/qos_excellence.go b/x/pairing/keeper/qos_excellence.go deleted file mode 100644 index ae58116dd3..0000000000 --- a/x/pairing/keeper/qos_excellence.go +++ /dev/null @@ -1,26 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/lavanet/lava/v4/utils" - pairingtypes "github.com/lavanet/lava/v4/x/pairing/types" -) - -// TODO: implement UpdateProviderQos(payments) - -// GetQos gets a provider's QoS excellence report from the providerQosFS -func (k Keeper) GetQos(ctx sdk.Context, chainID string, cluster string, provider string) (pairingtypes.QualityOfServiceReport, error) { - var qos pairingtypes.QualityOfServiceReport - key := pairingtypes.ProviderQosKey(provider, chainID, cluster) - found := k.providerQosFS.FindEntry(ctx, key, uint64(ctx.BlockHeight()), &qos) - if !found { - return qos, utils.LavaFormatWarning("provider of chain and cluster was not found in the store", fmt.Errorf("qos not found"), - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - utils.Attribute{Key: "cluster", Value: cluster}, - ) - } - return qos, nil -} diff --git a/x/pairing/keeper/qos_excellence_test.go b/x/pairing/keeper/qos_excellence_test.go deleted file mode 100644 index dcf145278f..0000000000 --- a/x/pairing/keeper/qos_excellence_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package keeper_test - -import ( - "testing" -) - -// TODO: All tests are not implemented since providerQosFS update and Qos score are not implemented yet - -// TestProviderQosMap checks that getting a providers' Qos map for specific chainID and cluster works properly -func TestProviderQosMap(t *testing.T) { -} - -// TestGetQos checks that using GetQos() returns the right Qos -func TestGetQos(t *testing.T) { -} - -// TestQosReqForSlots checks that if Qos req is active, all slots are assigned with Qos req -func TestQosReqForSlots(t *testing.T) { -} - -// TestQosScoreCluster that consumer pairing uses the correct cluster for QoS score calculations. -func TestQosScoreCluster(t *testing.T) { -} - -// TestQosScore checks that the qos score component is as expected (score == ComputeQos(), new users (sub usage less -// than a month) are not infuenced by Qos score, invalid Qos score == 1) -func TestQosScore(t *testing.T) { -} - -// TestUpdateClusteringCriteria checks that updating the clustering criteria doesn't make different version clusters to be mixed -func TestUpdateClusteringCriteria(t *testing.T) { -} diff --git a/x/pairing/keeper/reputation.go b/x/pairing/keeper/reputation.go new file mode 100644 index 0000000000..273603cad6 --- /dev/null +++ b/x/pairing/keeper/reputation.go @@ -0,0 +1,427 @@ +package keeper + +import ( + "fmt" + "sort" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v4/utils" + "github.com/lavanet/lava/v4/x/pairing/types" +) + +/* + +Reputation is a provider performance metric calculated using QoS excellence reports that are retrieved from relay payments. +Higher reputation improves the provider's chance to be picked in the pairing mechanism. + +The reputations are kept within a map called "reputations" in the keeper. The map's keys are a collection of the chain ID, +cluster, and the provider address. + +The reputation's pairing score is kept in the reputations fixation store so pairing queries will be deterministic for +past blocks. + +*/ + +// GetReputation gets a Reputation from the store +func (k Keeper) GetReputation(ctx sdk.Context, chainID string, cluster string, provider string) (types.Reputation, bool) { + key := types.ReputationKey(chainID, cluster, provider) + r, err := k.reputations.Get(ctx, key) + if err != nil { + utils.LavaFormatWarning("GetReputation: reputation not found", err, + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", provider), + ) + return types.Reputation{}, false + } + + return r, true +} + +// SetReputation sets a Reputation in the store +func (k Keeper) SetReputation(ctx sdk.Context, chainID string, cluster string, provider string, r types.Reputation) { + key := types.ReputationKey(chainID, cluster, provider) + err := k.reputations.Set(ctx, key, r) + if err != nil { + panic(fmt.Errorf("SetReputation: failed to set entry with key %v, error: %w", key, err)) + } +} + +// RemoveReputation removes a Reputation from the store +func (k Keeper) RemoveReputation(ctx sdk.Context, chainID string, cluster string, provider string) { + key := types.ReputationKey(chainID, cluster, provider) + err := k.reputations.Remove(ctx, key) + if err != nil { + panic(fmt.Errorf("RemoveReputation: failed to remove entry with key %v, error: %w", key, err)) + } +} + +// GetAllReputation gets all the reputation entries from the store for genesis +func (k Keeper) GetAllReputation(ctx sdk.Context) []types.ReputationGenesis { + iter, err := k.reputations.Iterate(ctx, nil) + if err != nil { + panic(fmt.Errorf("GetAllReputation: Failed to create iterator, error: %w", err)) + } + defer iter.Close() + + entries := []types.ReputationGenesis{} + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + panic(fmt.Errorf("GetAllReputation: Failed to get key from iterator, error: %w", err)) + } + + entry, err := k.reputations.Get(ctx, key) + if err != nil { + panic(fmt.Errorf("GetAllReputation: Failed to get entry with key %v, error: %w", key, err)) + } + + entries = append(entries, types.ReputationGenesis{ + ChainId: key.K1(), + Cluster: key.K2(), + Provider: key.K3(), + Reputation: entry, + }) + } + + return entries +} + +// UpdateReputationEpochQosScore updates the epoch QoS score of the provider's reputation using the score from the relay +// payment's QoS excellence report +func (k Keeper) UpdateReputationEpochQosScore(ctx sdk.Context, chainID string, cluster string, provider string, score math.LegacyDec, weight int64, stake sdk.Coin) { + // get current reputation and get parameters for the epoch score update + r, found := k.GetReputation(ctx, chainID, cluster, provider) + truncate := false + if found { + stabilizationPeriod := k.ReputationVarianceStabilizationPeriod(ctx) + if r.ShouldTruncate(stabilizationPeriod, ctx.BlockTime().UTC().Unix()) { + truncate = true + } + } else { + // new reputation score is not truncated and its decay factor is equal to 1 + r = types.NewReputation(ctx) + } + + // calculate the updated QoS epoch score + r.EpochScore.Update(score, truncate, weight) + + // update the reputation's stake and set + r.Stake = stake + k.SetReputation(ctx, chainID, cluster, provider, r) +} + +type ProviderQosScore struct { + Provider string + Score types.QosScore + Stake sdk.Coin +} + +type StakeProviderScores struct { + ProviderScores []ProviderQosScore + TotalStake sdk.Coin +} + +// UpdateAllReputationQosScore updates all the reputations on epoch start with the epoch score aggregated over the epoch +func (k Keeper) UpdateAllReputationQosScore(ctx sdk.Context) { + // scores is a map of "chainID cluster" -> stakeProviderScores + // it will be used to compare providers QoS scores within the same chain ID and cluster and determine + // the providers' reputation pairing score. + // note, the map is already sorted by QoS score in ascending order. + scores, err := k.UpdateReputationsForEpochStart(ctx) + if err != nil { + utils.LavaFormatError("critical: UpdateReputationQosScore: could not update providers QoS scores", err) + } + + // sort keys + keys := []types.ReputationChainClusterKey{} + for key := range scores { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { + return keys[i].ChainID+" "+keys[i].Cluster < keys[j].ChainID+" "+keys[j].Cluster + }) + + // iterate over providers QoS scores with the same chain ID and cluster + for _, chainCluster := range keys { + stakeProvidersScore := scores[chainCluster] + chainID, cluster := chainCluster.ChainID, chainCluster.Cluster + + // get benchmark score value. It's used as a percentile score to determine the reputation pairing score + // of each provider in a scale of [MinReputationPairingScore, MaxReputationPairingScore] (currently 0.5-2). + // to clarify, providers score are determined by their QoS score compared to the other providers that share the same chain ID and cluster. + // the benchmark score is the score of the provider that has the lowest QoS score within the 90th percentile + // of the providers in the chain ID and cluster. + benchmark, err := k.GetBenchmarkReputationScore(stakeProvidersScore) + if err != nil { + utils.LavaFormatError("critical: UpdateReputationQosScore: could not get benchmark QoS score", err) + } + + // set reputation pairing score by the benchmark + err = k.setReputationPairingScoreByBenchmark(ctx, chainID, cluster, benchmark, stakeProvidersScore.ProviderScores) + if err != nil { + utils.LavaFormatError("critical: UpdateReputationQosScore: could not set repuatation pairing scores", err) + } + } +} + +// UpdateReputationsForEpochStart does the following for each reputation: +// 1. applies time decay +// 2. resets the reputation epoch score +// 3. updates it last update time +// 4. add it to the scores map +func (k Keeper) UpdateReputationsForEpochStart(ctx sdk.Context) (map[types.ReputationChainClusterKey]StakeProviderScores, error) { + halfLifeFactor := k.ReputationHalfLifeFactor(ctx) + currentTime := ctx.BlockTime().UTC().Unix() + + scores := map[types.ReputationChainClusterKey]StakeProviderScores{} + + // iterate over all reputations + iter, err := k.reputations.Iterate(ctx, nil) + if err != nil { + return nil, utils.LavaFormatError("updateReputationsScores: failed to create reputations iterator", err) + } + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + return nil, utils.LavaFormatError("updateReputationsScores: failed to get reputation key from iterator", err) + } + chainID := key.K1() + cluster := key.K2() + provider := key.K3() + + reputation, err := iter.Value() + if err != nil { + return nil, utils.LavaFormatError("updateReputationsScores: failed to get reputation from iterator", err, + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", provider), + ) + } + + if reputation.EpochScore == types.ZeroQosScore { + // if the epoch score is zero, we don't need to update the reputation + utils.LavaFormatWarning("updateReputationsScores: epoch score is zero, skipping update", nil, + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", provider), + ) + continue + } + + // apply time decay on current score and add the epoch score (which is reset right after) + reputation, err = reputation.ApplyTimeDecayAndUpdateScore(utils.SafeUint64ToInt64Convert(halfLifeFactor), currentTime) + if err != nil { + return nil, utils.LavaFormatError("updateReputationsScores: apply time decay and update reputation", err, + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", provider), + ) + } + + // reset epoch score, update last update time and set the reputation + reputation.EpochScore = types.ZeroQosScore + reputation.TimeLastUpdated = currentTime + k.SetReputation(ctx, chainID, cluster, provider, reputation) + + // add entry to the scores map + providerScores, ok := scores[types.ReputationChainClusterKey{ChainID: chainID, Cluster: cluster}] + if !ok { + providerScores.ProviderScores = []ProviderQosScore{{Provider: provider, Score: reputation.Score, Stake: reputation.Stake}} + providerScores.TotalStake = reputation.Stake + } else { + providerScores.ProviderScores = append(providerScores.ProviderScores, ProviderQosScore{Provider: provider, Score: reputation.Score, Stake: reputation.Stake}) + providerScores.TotalStake = providerScores.TotalStake.Add(reputation.Stake) + } + scores[types.ReputationChainClusterKey{ChainID: chainID, Cluster: cluster}] = providerScores + } + + // in the provider scoring process, each provider is scored by its QoS score compared to the other providers + // that share the same chain ID and cluster. + // sortProviderScores() sorts the providers by their QoS score in ascending order so that the best score is first + // (low is better). This allows us to get the benchmark score more easily. + sortProviderScores(scores) + return scores, nil +} + +// sortProviderScores sorts the stakeProviderScores map score slices in ascending order +func sortProviderScores(scores map[types.ReputationChainClusterKey]StakeProviderScores) { + for chainCluster, stakeProviderScores := range scores { + chainID, cluster := chainCluster.ChainID, chainCluster.Cluster + + sort.Slice(stakeProviderScores.ProviderScores, func(i, j int) bool { + iScore, err := stakeProviderScores.ProviderScores[i].Score.Score.Resolve() + if err != nil { + panic(utils.LavaFormatError("UpdateReputationQosScore: cannot sort provider scores", err, + utils.LogAttr("provider", stakeProviderScores.ProviderScores[i].Provider), + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + )) + } + + jScore, err := stakeProviderScores.ProviderScores[j].Score.Score.Resolve() + if err != nil { + panic(utils.LavaFormatError("UpdateReputationQosScore: cannot sort provider scores", err, + utils.LogAttr("provider", stakeProviderScores.ProviderScores[j].Provider), + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + )) + } + + // if scores are equal, determine order by address + if iScore.Equal(jScore) { + iProvider := stakeProviderScores.ProviderScores[i].Provider + jProvider := stakeProviderScores.ProviderScores[j].Provider + return iProvider < jProvider + } + + return iScore.LT(jScore) + }) + } +} + +// GetBenchmarkReputationScore gets the score that will be used as the normalization factor when converting +// the provider's QoS score to the reputation pairing score. +// To do that, we go over all the QoS scores of providers that share chain ID and cluster from the lowest +// score to the highest (that input stakeProviderScores are sorted). We aggregate the providers stake until +// we pass totalStake * ReputationPairingScoreBenchmarkStakeThreshold (currently equal to 10% of total stake). +// Then, we return the last provider's score as the benchmark +func (k Keeper) GetBenchmarkReputationScore(stakeProviderScores StakeProviderScores) (math.LegacyDec, error) { + threshold := types.ReputationPairingScoreBenchmarkStakeThreshold.MulInt(stakeProviderScores.TotalStake.Amount) + aggregatedStake := sdk.ZeroDec() + scoreBenchmarkIndex := 0 + for i, providerScore := range stakeProviderScores.ProviderScores { + aggregatedStake = aggregatedStake.Add(providerScore.Stake.Amount.ToLegacyDec()) + if aggregatedStake.GTE(threshold) { + scoreBenchmarkIndex = i + break + } + } + + benchmark, err := stakeProviderScores.ProviderScores[scoreBenchmarkIndex].Score.Score.Resolve() + if err != nil { + return sdk.ZeroDec(), utils.LavaFormatError("getBenchmarkReputationScore: could not resolve benchmark score", err) + } + + return benchmark, nil +} + +// setReputationPairingScoreByBenchmark sets the reputation pairing score using a benchmark score for all providers +// with the same chain ID and cluster. +// The reputation pairing scores are determined as follows: if the provider's QoS score is smaller than the benchmark, +// it gets the max reputation pairing score. If not, it's normalized by the benchmark and scaled to fit the range +// [MinReputationPairingScore, MaxReputationPairingScore]. Note, smaller scores are better. +// To scale, we use the following formula: scaled_score = min_score + (max_score - min_score) * (benchmark / score) +// We divide (benchmark / score) and not the other way around since we expect that benchmark < score. +func (k Keeper) setReputationPairingScoreByBenchmark(ctx sdk.Context, chainID string, cluster string, benchmark math.LegacyDec, scores []ProviderQosScore) error { + if benchmark.IsNegative() { + return utils.LavaFormatError("setReputationPairingScoreByBenchmark: cannot set reputation pairing score with benchmark", fmt.Errorf("benchmark is negative"), + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("benchmark", benchmark.String()), + ) + } + + scale := types.MaxReputationPairingScore.Sub(types.MinReputationPairingScore) + for _, providerScore := range scores { + score, err := providerScore.Score.Score.Resolve() + if err != nil { + return utils.LavaFormatError("setReputationPairingScoreByBenchmark: cannot resolve provider score", err, + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", providerScore.Provider), + ) + } + + if score.IsNegative() { + return utils.LavaFormatError("setReputationPairingScoreByBenchmark: invalid provider score", fmt.Errorf("score is negative"), + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", providerScore.Provider), + utils.LogAttr("score", score.String()), + ) + } + + scaledScore := types.MinReputationPairingScore + if score.IsZero() || score.LTE(benchmark) { + // since the benchmark is a very high percentile score, we set the scaled score to the max + // for providers that have a score lower than the benchmark. + scaledScore = types.MaxReputationPairingScore + } else if score.GT(benchmark) { + scaledScore = types.MinReputationPairingScore.Add((benchmark.Quo(score)).Mul(scale)) + } + + err = k.SetReputationScore(ctx, chainID, cluster, providerScore.Provider, scaledScore) + if err != nil { + return utils.LavaFormatError("setReputationPairingScoreByBenchmark: set reputation pairing score failed", err, + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", providerScore.Provider), + ) + } + } + + return nil +} + +// GetReputationScore returns the current reputation pairing score +func (k Keeper) GetReputationScore(ctx sdk.Context, chainID string, cluster string, provider string) (val math.LegacyDec, found bool) { + block := uint64(ctx.BlockHeight()) + key := types.ReputationScoreKey(chainID, cluster, provider) + + var score types.ReputationPairingScore + found = k.reputationsFS.FindEntry(ctx, key, block, &score) + + return score.Score, found +} + +// GetReputationScore returns a reputation pairing score in a specific block +func (k Keeper) GetReputationScoreForBlock(ctx sdk.Context, chainID string, cluster string, provider string, block uint64) (val math.LegacyDec, entryBlock uint64, found bool) { + var score types.ReputationPairingScore + key := types.ReputationScoreKey(chainID, cluster, provider) + + entryBlock, _, _, found = k.reputationsFS.FindEntryDetailed(ctx, key, block, &score) + return score.Score, entryBlock, found +} + +// SetReputationScore sets a reputation pairing score +func (k Keeper) SetReputationScore(ctx sdk.Context, chainID string, cluster string, provider string, score math.LegacyDec) error { + key := types.ReputationScoreKey(chainID, cluster, provider) + reputationScore := types.ReputationPairingScore{Score: score} + err := k.reputationsFS.AppendEntry(ctx, key, uint64(ctx.BlockHeight()), &reputationScore) + if err != nil { + return utils.LavaFormatError("SetReputationScore: set reputation pairing score failed", err, + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", provider), + utils.LogAttr("score", score.String()), + ) + } + + return nil +} + +// RemoveReputationScore removes a reputation pairing score +func (k Keeper) RemoveReputationScore(ctx sdk.Context, chainID string, cluster string, provider string) error { + block := uint64(ctx.BlockHeight()) + nextEpoch, err := k.epochStorageKeeper.GetNextEpoch(ctx, block) + if err != nil { + return utils.LavaFormatError("RemoveReputationScore: get next epoch failed", err, + utils.LogAttr("block", block), + ) + } + key := types.ReputationScoreKey(chainID, cluster, provider) + + err = k.reputationsFS.DelEntry(ctx, key, nextEpoch) + if err != nil { + return utils.LavaFormatError("RemoveReputationScore: delete score failed", err, + utils.LogAttr("chain_id", chainID), + utils.LogAttr("cluster", cluster), + utils.LogAttr("provider", provider), + ) + } + return nil +} diff --git a/x/pairing/keeper/reputation_test.go b/x/pairing/keeper/reputation_test.go new file mode 100644 index 0000000000..b20b09b906 --- /dev/null +++ b/x/pairing/keeper/reputation_test.go @@ -0,0 +1,143 @@ +package keeper_test + +import ( + "strconv" + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + keepertest "github.com/lavanet/lava/v4/testutil/keeper" + commontypes "github.com/lavanet/lava/v4/utils/common/types" + "github.com/lavanet/lava/v4/x/pairing/keeper" + "github.com/lavanet/lava/v4/x/pairing/types" + "github.com/stretchr/testify/require" +) + +func createNReputations(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Reputation { + items := make([]types.Reputation, n) + for i := range items { + decIndex := math.LegacyNewDec(int64(i + 1)) + strIndex := strconv.Itoa(i) + items[i] = types.Reputation{ + Score: types.QosScore{ + Score: types.Frac{Num: decIndex, Denom: decIndex}, + Variance: types.Frac{Num: decIndex, Denom: decIndex}, + }, + EpochScore: types.QosScore{ + Score: types.Frac{Num: decIndex, Denom: decIndex}, + Variance: types.Frac{Num: decIndex, Denom: decIndex}, + }, + TimeLastUpdated: int64(i), + CreationTime: int64(i), + Stake: sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(int64(i))), + } + keeper.SetReputation(ctx, strIndex, strIndex, strIndex, items[i]) + } + return items +} + +func TestGetReputation(t *testing.T) { + keeper, ctx := keepertest.PairingKeeper(t) + items := createNReputations(keeper, ctx, 10) + for i, item := range items { + strIndex := strconv.Itoa(i) + entry, found := keeper.GetReputation(ctx, strIndex, strIndex, strIndex) + require.True(t, found) + require.True(t, item.Equal(entry)) + } + + _, found := keeper.GetReputation(ctx, "dummy", "dummy", "dummy") + require.False(t, found) +} + +func TestRemoveReputation(t *testing.T) { + keeper, ctx := keepertest.PairingKeeper(t) + items := createNReputations(keeper, ctx, 10) + for i := range items { + strIndex := strconv.Itoa(i) + keeper.RemoveReputation(ctx, strIndex, strIndex, strIndex) + _, found := keeper.GetReputation(ctx, strIndex, strIndex, strIndex) + require.False(t, found) + } +} + +func TestGetAllReputations(t *testing.T) { + keeper, ctx := keepertest.PairingKeeper(t) + items := createNReputations(keeper, ctx, 10) + genEntries := keeper.GetAllReputation(ctx) + for i := range genEntries { + require.True(t, items[i].Equal(genEntries[i].Reputation)) + } +} + +func createNReputationsScores(keeper *keeper.Keeper, ctx sdk.Context, n int) ([]math.LegacyDec, sdk.Context) { + items := make([]math.LegacyDec, n) + height := ctx.BlockHeight() + for i := range items { + decIndex := math.LegacyNewDec(int64(i + 1)) + strIndex := strconv.Itoa(i) + + err := keeper.SetReputationScore(ctx, strIndex, strIndex, strIndex, decIndex) + if err != nil { + panic(err) + } + + items[i] = decIndex + height++ + ctx = ctx.WithBlockHeight(height) + } + return items, ctx +} + +func TestGetReputationScore(t *testing.T) { + keeper, ctx := keepertest.PairingKeeper(t) + items, ctx := createNReputationsScores(keeper, ctx, 10) + for i, item := range items { + strIndex := strconv.Itoa(i) + entry, found := keeper.GetReputationScore(ctx, strIndex, strIndex, strIndex) + require.True(t, found) + require.True(t, item.Equal(entry)) + } + + _, found := keeper.GetReputationScore(ctx, "dummy", "dummy", "dummy") + require.False(t, found) +} + +func TestGetReputationScoreForBlock(t *testing.T) { + keeper, ctx := keepertest.PairingKeeper(t) + items, ctx := createNReputationsScores(keeper, ctx, 10) + for i, item := range items { + strIndex := strconv.Itoa(i) + entry, entryBlock, found := keeper.GetReputationScoreForBlock(ctx, strIndex, strIndex, strIndex, uint64(ctx.BlockHeight())) + require.True(t, found) + require.True(t, item.Equal(entry)) + require.Equal(t, uint64(i), entryBlock) + } + + _, _, found := keeper.GetReputationScoreForBlock(ctx, "dummy", "dummy", "dummy", uint64(ctx.BlockHeight())) + require.False(t, found) + _, _, found = keeper.GetReputationScoreForBlock(ctx, "2", "2", "2", 1) + require.False(t, found) +} + +func TestRemoveReputationScore(t *testing.T) { + ts := newTester(t) + keeper, ctx := ts.Keepers.Pairing, ts.Ctx + items, ctx := createNReputationsScores(&keeper, ctx, 10) + for i := range items { + strIndex := strconv.Itoa(i) + err := keeper.RemoveReputationScore(ctx, strIndex, strIndex, strIndex) + require.NoError(t, err) + _, found := keeper.GetReputationScore(ctx, strIndex, strIndex, strIndex) + require.True(t, found) + } + + ts.AdvanceEpoch() // removal applied + ts.AdvanceEpochUntilStale() // deletion happens + + for i := range items { + strIndex := strconv.Itoa(i) + _, found := keeper.GetReputationScore(ctx, strIndex, strIndex, strIndex) + require.False(t, found) + } +} diff --git a/x/pairing/keeper/scores/geo_req_test.go b/x/pairing/keeper/scores/geo_req_test.go index abcc07fcf6..1d034e302d 100644 --- a/x/pairing/keeper/scores/geo_req_test.go +++ b/x/pairing/keeper/scores/geo_req_test.go @@ -3,8 +3,8 @@ package scores import ( "testing" + "cosmossdk.io/math" epochstoragetypes "github.com/lavanet/lava/v4/x/epochstorage/types" - "github.com/lavanet/lava/v4/x/pairing/types" planstypes "github.com/lavanet/lava/v4/x/plans/types" "github.com/stretchr/testify/require" ) @@ -97,7 +97,7 @@ func TestGeoReqScore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { geoReq.Geo = tt.reqGeo stakeEntry.Geolocation = tt.providerGeo - pairingScore := NewPairingScore(&stakeEntry, types.QualityOfServiceReport{}) + pairingScore := NewPairingScore(&stakeEntry, math.LegacyZeroDec()) score := geoReq.Score(*pairingScore) require.True(t, score.Equal(calculateCostFromLatency(tt.expectedLatency))) }) diff --git a/x/pairing/keeper/scores/pairing_score.go b/x/pairing/keeper/scores/pairing_score.go index 3c7583fa96..07500f1ee5 100644 --- a/x/pairing/keeper/scores/pairing_score.go +++ b/x/pairing/keeper/scores/pairing_score.go @@ -3,7 +3,6 @@ package scores import ( "cosmossdk.io/math" epochstoragetypes "github.com/lavanet/lava/v4/x/epochstorage/types" - pairingtypes "github.com/lavanet/lava/v4/x/pairing/types" ) const ( @@ -13,12 +12,12 @@ 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.LegacyDec - ScoreComponents map[string]math.LegacyDec - SkipForSelection bool - SlotFiltering map[int]struct{} // slot indexes here are skipped - QosExcellenceReport pairingtypes.QualityOfServiceReport + Provider *epochstoragetypes.StakeEntry + Score math.LegacyDec + ScoreComponents map[string]math.LegacyDec + SkipForSelection bool + SlotFiltering map[int]struct{} // slot indexes here are skipped + reputationScore math.LegacyDec } func (ps *PairingScore) IsValidForSelection(slotIndex int) bool { @@ -40,13 +39,13 @@ func (ps *PairingScore) InvalidIndexes(possibleIndexes []int) []int { return invalidIndexes } -func NewPairingScore(stakeEntry *epochstoragetypes.StakeEntry, qos pairingtypes.QualityOfServiceReport) *PairingScore { +func NewPairingScore(stakeEntry *epochstoragetypes.StakeEntry, reputationScore math.LegacyDec) *PairingScore { score := PairingScore{ - Provider: stakeEntry, - Score: math.LegacyOneDec(), - ScoreComponents: map[string]math.LegacyDec{}, - SkipForSelection: false, - QosExcellenceReport: qos, + Provider: stakeEntry, + Score: math.LegacyOneDec(), + ScoreComponents: map[string]math.LegacyDec{}, + SkipForSelection: false, + reputationScore: reputationScore, } return &score } diff --git a/x/pairing/keeper/scores/qos_req.go b/x/pairing/keeper/scores/qos_req.go deleted file mode 100644 index ed86dfed9c..0000000000 --- a/x/pairing/keeper/scores/qos_req.go +++ /dev/null @@ -1,49 +0,0 @@ -package scores - -import ( - "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" - pairingtypes "github.com/lavanet/lava/v4/x/pairing/types" - planstypes "github.com/lavanet/lava/v4/x/plans/types" -) - -const qosReqName = "qos-req" - -type QosGetter interface { - GetQos(ctx sdk.Context, chainID string, cluster string, provider string) (pairingtypes.QualityOfServiceReport, error) -} - -// QosReq implements the ScoreReq interface for provider staking requirement(s) -type QosReq struct{} - -func (qr QosReq) Init(policy planstypes.Policy) bool { - return true -} - -// Score calculates the the provider's qos score -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 - - // qosScore, err := score.QosExcellenceReport.ComputeQoS() - // if err != nil { - // return math.NewUint(1) - // } - - // return math.Uint(qosScore) - return math.LegacyOneDec() -} - -func (qr QosReq) GetName() string { - return qosReqName -} - -// Equal used to compare slots to determine slot groups. -// Equal always returns true (there are no different "types" of qos) -func (qr QosReq) Equal(other ScoreReq) bool { - return true -} - -func (qr QosReq) GetReqForSlot(policy planstypes.Policy, slotIdx int) ScoreReq { - return qr -} diff --git a/x/pairing/keeper/scores/reputation_req.go b/x/pairing/keeper/scores/reputation_req.go new file mode 100644 index 0000000000..db16e94d8b --- /dev/null +++ b/x/pairing/keeper/scores/reputation_req.go @@ -0,0 +1,46 @@ +package scores + +import ( + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v4/x/pairing/types" + planstypes "github.com/lavanet/lava/v4/x/plans/types" +) + +const reputationReqName = "reputation-req" + +type ReputationGetter interface { + GetReputationScoreForBlock(ctx sdk.Context, chainID string, cluster string, provider string, block uint64) (val math.LegacyDec, entryBlock uint64, found bool) +} + +// ReputationReq implements the ScoreReq interface for provider staking requirement(s) +type ReputationReq struct{} + +func (rr ReputationReq) Init(policy planstypes.Policy) bool { + return true +} + +// Score gets the provider's reputation pairing score using the reputationFS fixation store +func (rr ReputationReq) Score(score PairingScore) math.LegacyDec { + if score.reputationScore.GT(types.MaxReputationPairingScore) { + return types.MaxReputationPairingScore + } else if score.reputationScore.LT(types.MinReputationPairingScore) { + return types.MinReputationPairingScore + } + + return score.reputationScore +} + +func (rr ReputationReq) GetName() string { + return reputationReqName +} + +// Equal used to compare slots to determine slot groups. +// Equal always returns true (there are no different "types" of reputation) +func (rr ReputationReq) Equal(other ScoreReq) bool { + return true +} + +func (rr ReputationReq) GetReqForSlot(policy planstypes.Policy, slotIdx int) ScoreReq { + return rr +} diff --git a/x/pairing/keeper/scores/score.go b/x/pairing/keeper/scores/score.go index a275fe6609..a1897d1d27 100644 --- a/x/pairing/keeper/scores/score.go +++ b/x/pairing/keeper/scores/score.go @@ -65,7 +65,7 @@ func GetAllReqs() []ScoreReq { return []ScoreReq{ &StakeReq{}, &GeoReq{}, - &QosReq{}, + // &ReputationReq{}, TODO: uncomment when you want to enable reputation influence on pairing } } diff --git a/x/pairing/types/QualityOfServiceReport.go b/x/pairing/types/QualityOfServiceReport.go index d5bb0b4836..1107688549 100644 --- a/x/pairing/types/QualityOfServiceReport.go +++ b/x/pairing/types/QualityOfServiceReport.go @@ -3,7 +3,9 @@ package types import ( "fmt" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v4/utils" ) func (qos *QualityOfServiceReport) ComputeQoS() (sdk.Dec, error) { @@ -24,3 +26,47 @@ func (qos *QualityOfServiceReport) ComputeQoSExcellence() (sdk.Dec, error) { } return qos.Availability.Quo(qos.Sync).Quo(qos.Latency).ApproxRoot(3) } + +// ComputeQosExcellenceForReputation computes the score from the QoS excellence report to update the provider's reputation +// report score = latency + sync*syncFactor + ((1/availability) - 1) * FailureCost (note: larger value is worse) +func (qos QualityOfServiceReport) ComputeQosExcellenceForReputation(syncFactor math.LegacyDec) (math.LegacyDec, error) { + if qos.Availability.LT(sdk.ZeroDec()) || + qos.Latency.LT(sdk.ZeroDec()) || + qos.Sync.LT(sdk.ZeroDec()) || syncFactor.LT(sdk.ZeroDec()) { + return sdk.ZeroDec(), utils.LavaFormatWarning("ComputeQosExcellenceForReputation: compute failed", fmt.Errorf("QoS excellence scores is below 0"), + utils.LogAttr("availability", qos.Availability.String()), + utils.LogAttr("sync", qos.Sync.String()), + utils.LogAttr("latency", qos.Latency.String()), + ) + } + + latency := qos.Latency + sync := qos.Sync.Mul(syncFactor) + availability := math.LegacyNewDec(FailureCost) + if !qos.Availability.IsZero() { + availability = availability.Mul((math.LegacyOneDec().Quo(qos.Availability)).Sub(math.LegacyOneDec())) + } else { + availability = math.LegacyMaxSortableDec.QuoInt64(2) // on qs.Availability = 0 we take the largest score possible + } + return latency.Add(sync).Add(availability), nil +} + +// ValidateAndFixQoSExcellence is a temporary function to validate the QoS excellence report +// TODO: remove after the optimizer refactor is merged +func (qos *QualityOfServiceReport) ValidateAndFixQoSExcellence() error { + if qos == nil { + return fmt.Errorf("QoS excellence report is nil") + } + + if qos.Availability.LT(sdk.ZeroDec()) { + qos.Availability = sdk.ZeroDec() + } + if qos.Latency.LT(sdk.ZeroDec()) { + qos.Latency = sdk.ZeroDec() + } + if qos.Sync.LT(sdk.ZeroDec()) { + qos.Sync = sdk.ZeroDec() + } + + return nil +} diff --git a/x/pairing/types/QualityOfServiceReport_test.go b/x/pairing/types/QualityOfServiceReport_test.go index 83a15ce6d6..9ed38e134d 100644 --- a/x/pairing/types/QualityOfServiceReport_test.go +++ b/x/pairing/types/QualityOfServiceReport_test.go @@ -3,11 +3,12 @@ package types import ( "testing" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" ) -func TestQosReport(t *testing.T) { +func createTestQosReportScores(forReputation bool) ([]math.LegacyDec, error) { qos1 := &QualityOfServiceReport{ Latency: sdk.MustNewDecFromStr("1.5"), Availability: sdk.MustNewDecFromStr("1"), @@ -29,20 +30,71 @@ func TestQosReport(t *testing.T) { Sync: sdk.MustNewDecFromStr("0.5"), } - qos1Res, errQos1 := qos1.ComputeQoSExcellence() - qos2Res, errQos2 := qos2.ComputeQoSExcellence() - qos3Res, errQos3 := qos3.ComputeQoSExcellence() - qos4Res, errQos4 := qos4.ComputeQoSExcellence() - require.NoError(t, errQos1) - require.NoError(t, errQos2) - require.NoError(t, errQos3) - require.NoError(t, errQos4) - require.True(t, qos1Res.LT(qos2Res)) - require.True(t, qos1Res.LT(qos3Res)) - require.True(t, qos1Res.LT(qos4Res)) - - require.True(t, qos2Res.GT(qos3Res)) - require.True(t, qos2Res.GT(qos4Res)) - - require.True(t, qos4Res.LT(qos3Res)) + res := []math.LegacyDec{} + if forReputation { + syncFactor := sdk.MustNewDecFromStr("0.5") + qos1Res, errQos1 := qos1.ComputeQosExcellenceForReputation(syncFactor) + if errQos1 != nil { + return nil, errQos1 + } + qos2Res, errQos2 := qos2.ComputeQosExcellenceForReputation(syncFactor) + if errQos2 != nil { + return nil, errQos2 + } + qos3Res, errQos3 := qos3.ComputeQosExcellenceForReputation(syncFactor) + if errQos3 != nil { + return nil, errQos3 + } + qos4Res, errQos4 := qos4.ComputeQosExcellenceForReputation(syncFactor) + if errQos4 != nil { + return nil, errQos4 + } + res = append(res, qos1Res, qos2Res, qos3Res, qos4Res) + } else { + qos1Res, errQos1 := qos1.ComputeQoSExcellence() + if errQos1 != nil { + return nil, errQos1 + } + qos2Res, errQos2 := qos2.ComputeQoSExcellence() + if errQos2 != nil { + return nil, errQos2 + } + qos3Res, errQos3 := qos3.ComputeQoSExcellence() + if errQos3 != nil { + return nil, errQos3 + } + qos4Res, errQos4 := qos4.ComputeQoSExcellence() + if errQos4 != nil { + return nil, errQos4 + } + res = append(res, qos1Res, qos2Res, qos3Res, qos4Res) + } + + return res, nil +} + +func TestQosReport(t *testing.T) { + res, err := createTestQosReportScores(false) + require.NoError(t, err) + require.True(t, res[0].LT(res[1])) + require.True(t, res[0].LT(res[2])) + require.True(t, res[0].LT(res[3])) + + require.True(t, res[1].GT(res[2])) + require.True(t, res[1].GT(res[3])) + + require.True(t, res[3].LT(res[2])) +} + +func TestQosReportForReputation(t *testing.T) { + res, err := createTestQosReportScores(true) + require.NoError(t, err) + require.True(t, res[0].GT(res[1])) + require.True(t, res[0].GT(res[2])) + require.True(t, res[0].LT(res[3])) + + require.True(t, res[1].LT(res[2])) + require.True(t, res[1].LT(res[3])) + + require.True(t, res[3].GT(res[2])) } diff --git a/x/pairing/types/genesis.go b/x/pairing/types/genesis.go index 430e2cc889..7718e1e057 100644 --- a/x/pairing/types/genesis.go +++ b/x/pairing/types/genesis.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "strings" fixationtypes "github.com/lavanet/lava/v4/x/fixationstore/types" timerstoretypes "github.com/lavanet/lava/v4/x/timerstore/types" @@ -17,8 +18,9 @@ func DefaultGenesis() *GenesisState { ProviderEpochCus: []ProviderEpochCuGenesis{}, ProviderConsumerEpochCus: []ProviderConsumerEpochCuGenesis{}, BadgeUsedCuList: []BadgeUsedCu{}, + Reputations: []ReputationGenesis{}, BadgesTS: *timerstoretypes.DefaultGenesis(), - ProviderQosFS: *fixationtypes.DefaultGenesis(), + ReputationScores: *fixationtypes.DefaultGenesis(), // this line is used by starport scaffolding # genesis/types/default Params: DefaultParams(), } @@ -73,6 +75,15 @@ func (gs GenesisState) Validate() error { return fmt.Errorf("badgeUsedCuList is not empty") } + reputationsIndexMap := map[string]struct{}{} + for _, elem := range gs.Reputations { + index := strings.Join([]string{elem.ChainId, elem.Cluster, elem.Provider}, " ") + if _, ok := reputationsIndexMap[index]; ok { + return fmt.Errorf("duplicated index for Reputations") + } + reputationsIndexMap[index] = struct{}{} + } + // this line is used by starport scaffolding # genesis/types/validate return gs.Params.Validate() diff --git a/x/pairing/types/genesis.pb.go b/x/pairing/types/genesis.pb.go index 8c6886d4a6..4c643bcc3e 100644 --- a/x/pairing/types/genesis.pb.go +++ b/x/pairing/types/genesis.pb.go @@ -82,11 +82,12 @@ type GenesisState struct { Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` BadgeUsedCuList []BadgeUsedCu `protobuf:"bytes,5,rep,name=badgeUsedCuList,proto3" json:"badgeUsedCuList"` BadgesTS types.GenesisState `protobuf:"bytes,6,opt,name=badgesTS,proto3" json:"badgesTS"` - ProviderQosFS types1.GenesisState `protobuf:"bytes,7,opt,name=providerQosFS,proto3" json:"providerQosFS"` UniqueEpochSessions []UniqueEpochSessionGenesis `protobuf:"bytes,8,rep,name=unique_epoch_sessions,json=uniqueEpochSessions,proto3" json:"unique_epoch_sessions"` ProviderEpochCus []ProviderEpochCuGenesis `protobuf:"bytes,9,rep,name=provider_epoch_cus,json=providerEpochCus,proto3" json:"provider_epoch_cus"` ProviderEpochComplainedCus []ProviderEpochComplainerCuGenesis `protobuf:"bytes,10,rep,name=provider_epoch_complained_cus,json=providerEpochComplainedCus,proto3" json:"provider_epoch_complained_cus"` ProviderConsumerEpochCus []ProviderConsumerEpochCuGenesis `protobuf:"bytes,11,rep,name=provider_consumer_epoch_cus,json=providerConsumerEpochCus,proto3" json:"provider_consumer_epoch_cus"` + Reputations []ReputationGenesis `protobuf:"bytes,12,rep,name=reputations,proto3" json:"reputations"` + ReputationScores types1.GenesisState `protobuf:"bytes,13,opt,name=reputation_scores,json=reputationScores,proto3" json:"reputation_scores"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -143,13 +144,6 @@ func (m *GenesisState) GetBadgesTS() types.GenesisState { return types.GenesisState{} } -func (m *GenesisState) GetProviderQosFS() types1.GenesisState { - if m != nil { - return m.ProviderQosFS - } - return types1.GenesisState{} -} - func (m *GenesisState) GetUniqueEpochSessions() []UniqueEpochSessionGenesis { if m != nil { return m.UniqueEpochSessions @@ -178,6 +172,20 @@ func (m *GenesisState) GetProviderConsumerEpochCus() []ProviderConsumerEpochCuGe return nil } +func (m *GenesisState) GetReputations() []ReputationGenesis { + if m != nil { + return m.Reputations + } + return nil +} + +func (m *GenesisState) GetReputationScores() types1.GenesisState { + if m != nil { + return m.ReputationScores + } + return types1.GenesisState{} +} + type UniqueEpochSessionGenesis struct { Epoch uint64 `protobuf:"varint,1,opt,name=epoch,proto3" json:"epoch,omitempty"` Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` @@ -466,6 +474,74 @@ func (m *ProviderConsumerEpochCuGenesis) GetProviderConsumerEpochCu() ProviderCo return ProviderConsumerEpochCu{} } +type ReputationGenesis struct { + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Cluster string `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` + Provider string `protobuf:"bytes,3,opt,name=provider,proto3" json:"provider,omitempty"` + Reputation Reputation `protobuf:"bytes,4,opt,name=reputation,proto3" json:"reputation"` +} + +func (m *ReputationGenesis) Reset() { *m = ReputationGenesis{} } +func (m *ReputationGenesis) String() string { return proto.CompactTextString(m) } +func (*ReputationGenesis) ProtoMessage() {} +func (*ReputationGenesis) Descriptor() ([]byte, []int) { + return fileDescriptor_dbd1e49b8b57595b, []int{6} +} +func (m *ReputationGenesis) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReputationGenesis) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReputationGenesis.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReputationGenesis) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReputationGenesis.Merge(m, src) +} +func (m *ReputationGenesis) XXX_Size() int { + return m.Size() +} +func (m *ReputationGenesis) XXX_DiscardUnknown() { + xxx_messageInfo_ReputationGenesis.DiscardUnknown(m) +} + +var xxx_messageInfo_ReputationGenesis proto.InternalMessageInfo + +func (m *ReputationGenesis) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *ReputationGenesis) GetCluster() string { + if m != nil { + return m.Cluster + } + return "" +} + +func (m *ReputationGenesis) GetProvider() string { + if m != nil { + return m.Provider + } + return "" +} + +func (m *ReputationGenesis) GetReputation() Reputation { + if m != nil { + return m.Reputation + } + return Reputation{} +} + func init() { proto.RegisterType((*BadgeUsedCu)(nil), "lavanet.lava.pairing.BadgeUsedCu") proto.RegisterType((*GenesisState)(nil), "lavanet.lava.pairing.GenesisState") @@ -473,6 +549,7 @@ func init() { proto.RegisterType((*ProviderEpochCuGenesis)(nil), "lavanet.lava.pairing.ProviderEpochCuGenesis") proto.RegisterType((*ProviderEpochComplainerCuGenesis)(nil), "lavanet.lava.pairing.ProviderEpochComplainerCuGenesis") proto.RegisterType((*ProviderConsumerEpochCuGenesis)(nil), "lavanet.lava.pairing.ProviderConsumerEpochCuGenesis") + proto.RegisterType((*ReputationGenesis)(nil), "lavanet.lava.pairing.ReputationGenesis") } func init() { @@ -480,51 +557,56 @@ func init() { } var fileDescriptor_dbd1e49b8b57595b = []byte{ - // 697 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xc1, 0x53, 0xd3, 0x4e, - 0x18, 0x6d, 0x68, 0x5a, 0xc2, 0x57, 0x7e, 0x3f, 0xcb, 0x8a, 0x12, 0x2a, 0xc4, 0x12, 0xc7, 0x99, - 0xe2, 0x68, 0x32, 0x83, 0x8c, 0x07, 0x8f, 0xa0, 0x32, 0xa0, 0x07, 0x69, 0x61, 0x9c, 0xf1, 0x12, - 0xd3, 0x64, 0x2d, 0xab, 0x34, 0x59, 0xb3, 0x49, 0x87, 0x9e, 0xfc, 0x17, 0xbc, 0x79, 0xf6, 0x1f, - 0xf1, 0xcc, 0x91, 0xa3, 0x27, 0xc7, 0x81, 0x83, 0x7f, 0x83, 0x37, 0x27, 0x9b, 0x6d, 0x69, 0xda, - 0x04, 0xc5, 0x71, 0x3c, 0x25, 0x9b, 0x7d, 0xdf, 0x7b, 0xdf, 0xcb, 0xbe, 0x6f, 0x16, 0xf4, 0x43, - 0xbb, 0x67, 0x7b, 0x38, 0x34, 0xe3, 0xa7, 0x49, 0x6d, 0x12, 0x10, 0xaf, 0x63, 0x76, 0xb0, 0x87, - 0x19, 0x61, 0x06, 0x0d, 0xfc, 0xd0, 0x47, 0xf3, 0x02, 0x63, 0xc4, 0x4f, 0x43, 0x60, 0x6a, 0xf3, - 0x1d, 0xbf, 0xe3, 0x73, 0x80, 0x19, 0xbf, 0x25, 0xd8, 0xda, 0x4a, 0x26, 0x1f, 0xb5, 0x03, 0xbb, - 0x2b, 0xe8, 0x6a, 0xb7, 0x32, 0x21, 0x98, 0xfa, 0xce, 0x81, 0xe5, 0x44, 0x02, 0xb4, 0x9a, 0x02, - 0xbd, 0x26, 0x47, 0x76, 0x48, 0x7c, 0x8f, 0x85, 0x7e, 0x80, 0x87, 0xab, 0x4c, 0xbe, 0x90, 0x74, - 0x71, 0x90, 0xe0, 0xf8, 0x6b, 0x02, 0xd2, 0x77, 0xa1, 0xb2, 0x61, 0xbb, 0x1d, 0xbc, 0xcf, 0xb0, - 0xbb, 0x19, 0xa1, 0x55, 0x98, 0x6b, 0xc7, 0x4b, 0x2b, 0x62, 0xd8, 0xb5, 0x9c, 0xc8, 0x7a, 0x8b, - 0xfb, 0xaa, 0x54, 0x97, 0x1a, 0xb3, 0xcd, 0xff, 0xdb, 0xe7, 0xb8, 0xa7, 0xb8, 0x8f, 0x16, 0x60, - 0x5a, 0x80, 0xd4, 0xa9, 0xba, 0xd4, 0x90, 0x9b, 0xe5, 0x88, 0xef, 0xe9, 0x1f, 0xcb, 0x30, 0xbb, - 0x95, 0xfc, 0xa8, 0x56, 0x68, 0x87, 0x18, 0x3d, 0x84, 0x72, 0x62, 0x94, 0x33, 0x55, 0xd6, 0x96, - 0x8c, 0xac, 0x1f, 0x67, 0x3c, 0xe7, 0x98, 0x0d, 0xf9, 0xf8, 0xeb, 0xcd, 0x42, 0x53, 0x54, 0xa0, - 0x5d, 0xb8, 0x32, 0xa2, 0xfb, 0x8c, 0xb0, 0x50, 0x2d, 0xd5, 0x8b, 0x8d, 0xca, 0xda, 0x4a, 0x36, - 0xc9, 0x88, 0x19, 0xc1, 0x34, 0x5e, 0x8f, 0xb6, 0x40, 0xe1, 0x9f, 0xd8, 0x5e, 0x4b, 0x2d, 0xf3, - 0x86, 0x6e, 0xa7, 0xb9, 0xce, 0x7f, 0x95, 0x31, 0xea, 0x43, 0xf0, 0x0d, 0x8b, 0xd1, 0x1e, 0xfc, - 0x47, 0x03, 0xbf, 0x47, 0x5c, 0x1c, 0xec, 0xfa, 0xec, 0x49, 0x4b, 0x9d, 0xe6, 0x6c, 0x8d, 0x34, - 0x5b, 0xea, 0x8c, 0xb2, 0x08, 0xd3, 0x24, 0x88, 0xc0, 0xb5, 0xc8, 0x23, 0xef, 0x22, 0x6c, 0x25, - 0x47, 0xcf, 0x30, 0x63, 0x71, 0xb9, 0xaa, 0x70, 0xdf, 0x66, 0xb6, 0xef, 0x7d, 0x5e, 0xf2, 0x38, - 0xae, 0x68, 0x25, 0x05, 0x42, 0x49, 0x88, 0x5c, 0x8d, 0x26, 0x00, 0x0c, 0xbd, 0x02, 0x34, 0xd0, - 0xb6, 0x06, 0x39, 0x63, 0xea, 0x0c, 0xd7, 0xb9, 0x9b, 0x73, 0x48, 0x02, 0xcf, 0x89, 0x36, 0xa3, - 0xb4, 0x48, 0x95, 0xa6, 0x77, 0x19, 0x7a, 0x0f, 0xcb, 0xe3, 0x0a, 0x7e, 0x97, 0x1e, 0xda, 0xc4, - 0xe3, 0xc9, 0x61, 0x2a, 0x70, 0xb1, 0x07, 0xbf, 0x23, 0x36, 0x28, 0x0c, 0xc6, 0x65, 0x6b, 0x34, - 0x13, 0xe7, 0xc6, 0x0d, 0xf4, 0xe1, 0xc6, 0xb0, 0x01, 0xc7, 0xf7, 0x58, 0xd4, 0x4d, 0x79, 0xad, - 0x70, 0xf9, 0xf5, 0x8b, 0xe5, 0x37, 0x45, 0x5d, 0xa6, 0x67, 0x95, 0x66, 0xa3, 0xd8, 0x8e, 0xac, - 0x4c, 0x55, 0x8b, 0x3b, 0xb2, 0x52, 0xac, 0xca, 0x3b, 0xb2, 0x22, 0x57, 0x4b, 0xfa, 0x27, 0x09, - 0x16, 0x73, 0x0f, 0x0a, 0xcd, 0x43, 0x89, 0x37, 0xc6, 0xa7, 0x44, 0x6e, 0x26, 0x0b, 0x54, 0x03, - 0x65, 0xa0, 0xc0, 0xe7, 0x6c, 0xa6, 0x39, 0x5c, 0x23, 0x15, 0xa6, 0x69, 0xe0, 0xbf, 0xc1, 0x4e, - 0xa8, 0x16, 0xf9, 0xd6, 0x60, 0x89, 0x16, 0x41, 0x71, 0x0e, 0x6c, 0xe2, 0x59, 0xc4, 0x55, 0xe5, - 0x64, 0x8b, 0xaf, 0xb7, 0x5d, 0xb4, 0x0c, 0x20, 0x22, 0x15, 0x6f, 0x96, 0xb8, 0xd6, 0x8c, 0xf8, - 0xb2, 0xed, 0xea, 0x9f, 0x25, 0xb8, 0x9e, 0x7d, 0xc8, 0x7f, 0xd0, 0xe0, 0x68, 0x1b, 0xc5, 0x74, - 0x1b, 0x2f, 0x60, 0x6e, 0x22, 0x7b, 0xbc, 0xd5, 0x89, 0x71, 0xcc, 0x89, 0xde, 0x60, 0xbc, 0xc7, - 0x32, 0xa7, 0x7f, 0x97, 0xa0, 0xfe, 0xab, 0xe0, 0xfc, 0x5d, 0x2b, 0x3d, 0x58, 0xca, 0x0b, 0x79, - 0x70, 0xee, 0xca, 0xbc, 0x64, 0xc6, 0x85, 0xbf, 0x45, 0x9a, 0x07, 0xd0, 0x7f, 0x48, 0xa0, 0x5d, - 0x9c, 0xd1, 0x7f, 0x95, 0x29, 0x0a, 0xb5, 0xfc, 0x29, 0xe3, 0x19, 0xab, 0xac, 0xdd, 0xbb, 0xd4, - 0x90, 0x09, 0xf7, 0x0b, 0x39, 0xd3, 0xb5, 0xf1, 0xe8, 0xf8, 0x54, 0x93, 0x4e, 0x4e, 0x35, 0xe9, - 0xdb, 0xa9, 0x26, 0x7d, 0x38, 0xd3, 0x0a, 0x27, 0x67, 0x5a, 0xe1, 0xcb, 0x99, 0x56, 0x78, 0x79, - 0xa7, 0x43, 0xc2, 0x83, 0xa8, 0x6d, 0x38, 0x7e, 0xd7, 0x4c, 0xdd, 0x80, 0xbd, 0x75, 0xf3, 0x68, - 0x78, 0xad, 0x86, 0x7d, 0x8a, 0x59, 0xbb, 0xcc, 0x2f, 0xc1, 0xfb, 0x3f, 0x03, 0x00, 0x00, 0xff, - 0xff, 0x20, 0x0a, 0x08, 0x66, 0xee, 0x07, 0x00, 0x00, + // 784 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcd, 0x6e, 0xd3, 0x4a, + 0x14, 0x8e, 0x1b, 0x27, 0x71, 0x4f, 0x7a, 0xef, 0x4d, 0xe6, 0x16, 0xea, 0x86, 0x36, 0xa4, 0x46, + 0x15, 0x29, 0x82, 0x58, 0x2a, 0x15, 0x0b, 0x96, 0x2d, 0x50, 0x35, 0x20, 0x41, 0x13, 0x2a, 0x24, + 0x58, 0x18, 0xc7, 0x1e, 0xd2, 0x81, 0xc6, 0x1e, 0x3c, 0x76, 0xd4, 0xac, 0x78, 0x05, 0x5e, 0x81, + 0x3d, 0xcf, 0xc0, 0xba, 0xcb, 0x2e, 0x59, 0x21, 0xd4, 0x2e, 0x78, 0x02, 0x16, 0xec, 0x90, 0xc7, + 0x93, 0x1f, 0x27, 0x4e, 0x4b, 0x11, 0x62, 0x95, 0x1c, 0xcf, 0x77, 0xbe, 0xef, 0x9c, 0xe3, 0xf3, + 0x8d, 0x0c, 0xda, 0x81, 0xd9, 0x35, 0x1d, 0xec, 0xeb, 0xe1, 0xaf, 0x4e, 0x4d, 0xe2, 0x11, 0xa7, + 0xad, 0xb7, 0xb1, 0x83, 0x19, 0x61, 0x35, 0xea, 0xb9, 0xbe, 0x8b, 0xe6, 0x05, 0xa6, 0x16, 0xfe, + 0xd6, 0x04, 0xa6, 0x34, 0xdf, 0x76, 0xdb, 0x2e, 0x07, 0xe8, 0xe1, 0xbf, 0x08, 0x5b, 0x5a, 0x49, + 0xe4, 0xa3, 0xa6, 0x67, 0x76, 0x04, 0x5d, 0xe9, 0x5a, 0x22, 0x04, 0x53, 0xd7, 0xda, 0x37, 0xac, + 0x40, 0x80, 0xd6, 0x62, 0xa0, 0x57, 0xe4, 0xd0, 0xf4, 0x89, 0xeb, 0x30, 0xdf, 0xf5, 0xf0, 0x20, + 0x4a, 0xe4, 0xf3, 0x49, 0x07, 0x7b, 0x11, 0x8e, 0xff, 0x15, 0xa0, 0xd5, 0x44, 0x51, 0x0f, 0xd3, + 0xc0, 0x1f, 0xe1, 0xd2, 0x76, 0x21, 0xbf, 0x69, 0xda, 0x6d, 0xbc, 0xc7, 0xb0, 0xbd, 0x15, 0xa0, + 0x35, 0x28, 0xb6, 0xc2, 0xd0, 0x08, 0x18, 0xb6, 0x0d, 0x2b, 0x30, 0xde, 0xe0, 0x9e, 0x2a, 0x55, + 0xa4, 0xea, 0x5c, 0xe3, 0xdf, 0xd6, 0x10, 0xf7, 0x10, 0xf7, 0xd0, 0x02, 0xe4, 0x04, 0x48, 0x9d, + 0xa9, 0x48, 0x55, 0xb9, 0x91, 0x0d, 0xf8, 0x99, 0xf6, 0x3d, 0x0b, 0x73, 0xdb, 0xd1, 0x3c, 0x9b, + 0xbe, 0xe9, 0x63, 0x74, 0x17, 0xb2, 0xd1, 0x3c, 0x38, 0x53, 0x7e, 0x7d, 0xa9, 0x96, 0x34, 0xdf, + 0xda, 0x13, 0x8e, 0xd9, 0x94, 0x8f, 0xbe, 0x5c, 0x4d, 0x35, 0x44, 0x06, 0xda, 0x85, 0xff, 0x46, + 0x74, 0x1f, 0x11, 0xe6, 0xab, 0x99, 0x4a, 0xba, 0x9a, 0x5f, 0x5f, 0x49, 0x26, 0x19, 0x69, 0x46, + 0x30, 0x8d, 0xe7, 0xa3, 0x6d, 0x50, 0xf8, 0x23, 0xf6, 0xb4, 0xa9, 0x66, 0x79, 0x41, 0xab, 0x71, + 0xae, 0xe1, 0x44, 0x6b, 0xa3, 0x7d, 0x08, 0xbe, 0x41, 0x32, 0x22, 0x70, 0x29, 0x70, 0xc8, 0xdb, + 0x00, 0x1b, 0xd1, 0xbb, 0x64, 0x98, 0xb1, 0xf0, 0x9d, 0xa9, 0x0a, 0xaf, 0x50, 0x4f, 0xae, 0x70, + 0x8f, 0xa7, 0xdc, 0x0f, 0x33, 0x9a, 0x51, 0x82, 0x10, 0x11, 0xfc, 0xff, 0x07, 0x13, 0x00, 0x86, + 0x5e, 0x02, 0xa2, 0x9e, 0xdb, 0x25, 0x36, 0xf6, 0x8c, 0xfe, 0xe2, 0x30, 0x75, 0x96, 0xeb, 0xdc, + 0x9c, 0x32, 0x4e, 0x81, 0xe7, 0x44, 0x5b, 0x41, 0x5c, 0xa4, 0x40, 0xe3, 0xa7, 0x0c, 0xbd, 0x83, + 0xe5, 0x71, 0x05, 0xb7, 0x43, 0x0f, 0x4c, 0xe2, 0xf0, 0x77, 0xcc, 0x54, 0xe0, 0x62, 0x77, 0x7e, + 0x45, 0xac, 0x9f, 0xe8, 0x8d, 0xcb, 0x96, 0x68, 0x22, 0xce, 0x0e, 0x0b, 0xe8, 0xc1, 0x95, 0x41, + 0x01, 0x96, 0xeb, 0xb0, 0xa0, 0x13, 0xeb, 0x35, 0xcf, 0xe5, 0x37, 0xce, 0x96, 0xdf, 0x12, 0x79, + 0x89, 0x3d, 0xab, 0x34, 0x19, 0xc5, 0xd0, 0x63, 0xc8, 0x0f, 0x8d, 0xc1, 0xd4, 0x39, 0x2e, 0x75, + 0x3d, 0x59, 0xaa, 0x31, 0x00, 0xc6, 0xd9, 0x47, 0x19, 0xd0, 0x0b, 0x28, 0x0e, 0x43, 0x83, 0x59, + 0xae, 0x87, 0x99, 0xfa, 0x0f, 0xdf, 0xb5, 0x6a, 0x9c, 0x36, 0x66, 0xf4, 0xa4, 0x75, 0x2b, 0x0c, + 0x89, 0x9a, 0x9c, 0xa7, 0x2e, 0x2b, 0x33, 0x85, 0x74, 0x5d, 0x56, 0xd2, 0x05, 0xb9, 0x2e, 0x2b, + 0x72, 0x21, 0x53, 0x97, 0x95, 0x5c, 0x41, 0xd1, 0x3e, 0x48, 0xb0, 0x38, 0x75, 0xb9, 0xd0, 0x3c, + 0x64, 0xf8, 0x30, 0xb9, 0x07, 0xe5, 0x46, 0x14, 0xa0, 0x12, 0x28, 0xfd, 0xa9, 0x70, 0x17, 0xcf, + 0x36, 0x06, 0x31, 0x52, 0x21, 0x47, 0x3d, 0xf7, 0x35, 0xb6, 0x7c, 0x35, 0xcd, 0x8f, 0xfa, 0x21, + 0x5a, 0x04, 0xc5, 0xda, 0x37, 0x89, 0x63, 0x10, 0x5b, 0x95, 0xa3, 0x23, 0x1e, 0xef, 0xd8, 0x68, + 0x19, 0x40, 0xd8, 0x20, 0x3c, 0xcc, 0x70, 0xad, 0x59, 0xf1, 0x64, 0xc7, 0xd6, 0x3e, 0x49, 0x70, + 0x39, 0x79, 0x31, 0x7f, 0xa3, 0xc0, 0xd1, 0x32, 0xd2, 0xf1, 0x32, 0x9e, 0x41, 0x71, 0xc2, 0x2f, + 0xbc, 0xd4, 0x09, 0xb3, 0x4f, 0xb1, 0x4b, 0xff, 0xf2, 0x18, 0xf3, 0x89, 0xf6, 0x4d, 0x82, 0xca, + 0x79, 0xcb, 0xfe, 0x67, 0x5b, 0xe9, 0xc2, 0xd2, 0x34, 0x63, 0x7a, 0xc3, 0xae, 0xf4, 0x0b, 0xfa, + 0x52, 0xf4, 0xb7, 0x48, 0xa7, 0x01, 0xb4, 0x1f, 0x12, 0x94, 0xcf, 0xf6, 0xd5, 0xdf, 0xda, 0x29, + 0x0a, 0xa5, 0xe9, 0x37, 0x03, 0xdf, 0xb1, 0xfc, 0xfa, 0xad, 0x0b, 0x5d, 0x0c, 0xa2, 0xfb, 0x85, + 0x29, 0x37, 0x82, 0xf6, 0x51, 0x82, 0xe2, 0x84, 0xd1, 0x63, 0x25, 0x4a, 0xf1, 0x12, 0x55, 0xc8, + 0x59, 0x07, 0x01, 0xf3, 0x07, 0x2d, 0xf7, 0xc3, 0xd8, 0x34, 0xd2, 0x63, 0xd3, 0x78, 0x00, 0x30, + 0x74, 0xb7, 0x78, 0x91, 0x95, 0xf3, 0xae, 0x1d, 0x51, 0xfb, 0x48, 0xe6, 0xe6, 0xbd, 0xa3, 0x93, + 0xb2, 0x74, 0x7c, 0x52, 0x96, 0xbe, 0x9e, 0x94, 0xa5, 0xf7, 0xa7, 0xe5, 0xd4, 0xf1, 0x69, 0x39, + 0xf5, 0xf9, 0xb4, 0x9c, 0x7a, 0x7e, 0xa3, 0x4d, 0xfc, 0xfd, 0xa0, 0x55, 0xb3, 0xdc, 0x8e, 0x1e, + 0xfb, 0x20, 0xe8, 0x6e, 0xe8, 0x87, 0x83, 0xaf, 0x02, 0xbf, 0x47, 0x31, 0x6b, 0x65, 0xf9, 0x17, + 0xc1, 0xed, 0x9f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x47, 0x7c, 0xb5, 0x22, 0x09, 0x00, 0x00, } func (m *BadgeUsedCu) Marshal() (dAtA []byte, err error) { @@ -582,6 +664,30 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.ReputationScores.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6a + if len(m.Reputations) > 0 { + for iNdEx := len(m.Reputations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Reputations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + } if len(m.ProviderConsumerEpochCus) > 0 { for iNdEx := len(m.ProviderConsumerEpochCus) - 1; iNdEx >= 0; iNdEx-- { { @@ -638,16 +744,6 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x42 } } - { - size, err := m.ProviderQosFS.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x3a { size, err := m.BadgesTS.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -902,6 +998,60 @@ func (m *ProviderConsumerEpochCuGenesis) MarshalToSizedBuffer(dAtA []byte) (int, return len(dAtA) - i, nil } +func (m *ReputationGenesis) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReputationGenesis) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReputationGenesis) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Reputation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Provider) > 0 { + i -= len(m.Provider) + copy(dAtA[i:], m.Provider) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Provider))) + i-- + dAtA[i] = 0x1a + } + if len(m.Cluster) > 0 { + i -= len(m.Cluster) + copy(dAtA[i:], m.Cluster) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Cluster))) + i-- + dAtA[i] = 0x12 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { offset -= sovGenesis(v) base := offset @@ -945,8 +1095,6 @@ func (m *GenesisState) Size() (n int) { } l = m.BadgesTS.Size() n += 1 + l + sovGenesis(uint64(l)) - l = m.ProviderQosFS.Size() - n += 1 + l + sovGenesis(uint64(l)) if len(m.UniqueEpochSessions) > 0 { for _, e := range m.UniqueEpochSessions { l = e.Size() @@ -971,6 +1119,14 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + if len(m.Reputations) > 0 { + for _, e := range m.Reputations { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.ReputationScores.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -1071,6 +1227,29 @@ func (m *ProviderConsumerEpochCuGenesis) Size() (n int) { return n } +func (m *ReputationGenesis) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.Cluster) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.Provider) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.Reputation.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + func sovGenesis(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1309,9 +1488,9 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 7: + case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProviderQosFS", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UniqueEpochSessions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1338,13 +1517,14 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.ProviderQosFS.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.UniqueEpochSessions = append(m.UniqueEpochSessions, UniqueEpochSessionGenesis{}) + if err := m.UniqueEpochSessions[len(m.UniqueEpochSessions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 8: + case 9: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UniqueEpochSessions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ProviderEpochCus", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1371,14 +1551,14 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.UniqueEpochSessions = append(m.UniqueEpochSessions, UniqueEpochSessionGenesis{}) - if err := m.UniqueEpochSessions[len(m.UniqueEpochSessions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.ProviderEpochCus = append(m.ProviderEpochCus, ProviderEpochCuGenesis{}) + if err := m.ProviderEpochCus[len(m.ProviderEpochCus)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 9: + case 10: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProviderEpochCus", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ProviderEpochComplainedCus", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1405,14 +1585,14 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ProviderEpochCus = append(m.ProviderEpochCus, ProviderEpochCuGenesis{}) - if err := m.ProviderEpochCus[len(m.ProviderEpochCus)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.ProviderEpochComplainedCus = append(m.ProviderEpochComplainedCus, ProviderEpochComplainerCuGenesis{}) + if err := m.ProviderEpochComplainedCus[len(m.ProviderEpochComplainedCus)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 10: + case 11: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProviderEpochComplainedCus", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ProviderConsumerEpochCus", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1439,14 +1619,14 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ProviderEpochComplainedCus = append(m.ProviderEpochComplainedCus, ProviderEpochComplainerCuGenesis{}) - if err := m.ProviderEpochComplainedCus[len(m.ProviderEpochComplainedCus)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.ProviderConsumerEpochCus = append(m.ProviderConsumerEpochCus, ProviderConsumerEpochCuGenesis{}) + if err := m.ProviderConsumerEpochCus[len(m.ProviderConsumerEpochCus)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 11: + case 12: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProviderConsumerEpochCus", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Reputations", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1473,8 +1653,41 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ProviderConsumerEpochCus = append(m.ProviderConsumerEpochCus, ProviderConsumerEpochCuGenesis{}) - if err := m.ProviderConsumerEpochCus[len(m.ProviderConsumerEpochCus)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Reputations = append(m.Reputations, ReputationGenesis{}) + if err := m.Reputations[len(m.Reputations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReputationScores", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ReputationScores.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -2213,6 +2426,185 @@ func (m *ProviderConsumerEpochCuGenesis) Unmarshal(dAtA []byte) error { } return nil } +func (m *ReputationGenesis) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReputationGenesis: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReputationGenesis: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cluster = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Provider", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Provider = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reputation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Reputation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipGenesis(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/pairing/types/genesis_test.go b/x/pairing/types/genesis_test.go index 0a18e82419..f8b42cee83 100644 --- a/x/pairing/types/genesis_test.go +++ b/x/pairing/types/genesis_test.go @@ -82,6 +82,20 @@ func TestGenesisState_Validate(t *testing.T) { ProviderConsumerEpochCu: types.ProviderConsumerEpochCu{Cu: 20}, }, }, + 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 # types/genesis/validField }, valid: true, @@ -175,6 +189,27 @@ func TestGenesisState_Validate(t *testing.T) { }, valid: false, }, + { + desc: "duplicated reputations", + genState: &types.GenesisState{ + Params: types.DefaultParams(), + Reputations: []types.ReputationGenesis{ + { + ChainId: "0", + Cluster: "0", + Provider: "0", + Reputation: types.Reputation{}, + }, + { + ChainId: "0", + Cluster: "0", + Provider: "0", + Reputation: types.Reputation{}, + }, + }, + }, + valid: false, + }, // this line is used by starport scaffolding # types/genesis/testcase } { t.Run(tc.desc, func(t *testing.T) { diff --git a/x/pairing/types/params.go b/x/pairing/types/params.go index bbc76ee1ff..e654bb7073 100644 --- a/x/pairing/types/params.go +++ b/x/pairing/types/params.go @@ -38,8 +38,8 @@ var ( ) var ( - KeyReputationHalfLifeFactor = []byte("ReputationHalfLifeFactor") - DefaultReputationHalfLifeFactor int64 = 12 * 30 * 24 * 60 * 60 // year in seconds + KeyReputationHalfLifeFactor = []byte("ReputationHalfLifeFactor") + DefaultReputationHalfLifeFactor uint64 = 12 * 30 * 24 * 60 * 60 // year in seconds ) var ( @@ -59,7 +59,7 @@ func NewParams( recommendedEpochNumToCollectPayment uint64, reputationVarianceStabilizationPeriod int64, reputationLatencyOverSyncFactor math.LegacyDec, - reputationHalfLifeFactor int64, + reputationHalfLifeFactor uint64, reputationRelayFailureCost uint64, ) Params { return Params{ @@ -205,7 +205,7 @@ func validateReputationLatencyOverSyncFactor(v interface{}) error { // validateReputationHalfLifeFactor validates the ReputationHalfLifeFactor param func validateReputationHalfLifeFactor(v interface{}) error { - reputationHalfLifeFactor, ok := v.(int64) + reputationHalfLifeFactor, ok := v.(uint64) if !ok { return fmt.Errorf("invalid parameter type: %T", v) } diff --git a/x/pairing/types/params.pb.go b/x/pairing/types/params.pb.go index a4311ca79b..07bf7c4eb6 100644 --- a/x/pairing/types/params.pb.go +++ b/x/pairing/types/params.pb.go @@ -31,7 +31,7 @@ type Params struct { RecommendedEpochNumToCollectPayment uint64 `protobuf:"varint,14,opt,name=recommendedEpochNumToCollectPayment,proto3" json:"recommendedEpochNumToCollectPayment,omitempty" yaml:"recommended_epoch_num_to_collect_payment"` ReputationVarianceStabilizationPeriod int64 `protobuf:"varint,15,opt,name=reputation_variance_stabilization_period,json=reputationVarianceStabilizationPeriod,proto3" json:"reputation_variance_stabilization_period,omitempty" yaml:"reputation_variance_stabilization_period"` ReputationLatencyOverSyncFactor github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,16,opt,name=reputation_latency_over_sync_factor,json=reputationLatencyOverSyncFactor,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"reputation_latency_over_sync_factor" yaml:"reputation_latency_over_sync_factor"` - ReputationHalfLifeFactor int64 `protobuf:"varint,17,opt,name=reputation_half_life_factor,json=reputationHalfLifeFactor,proto3" json:"reputation_half_life_factor,omitempty" yaml:"reputation_half_life_factor"` + ReputationHalfLifeFactor uint64 `protobuf:"varint,17,opt,name=reputation_half_life_factor,json=reputationHalfLifeFactor,proto3" json:"reputation_half_life_factor,omitempty" yaml:"reputation_half_life_factor"` ReputationRelayFailureCost uint64 `protobuf:"varint,18,opt,name=reputation_relay_failure_cost,json=reputationRelayFailureCost,proto3" json:"reputation_relay_failure_cost,omitempty" yaml:"reputation_relay_failure_cost"` } @@ -88,7 +88,7 @@ func (m *Params) GetReputationVarianceStabilizationPeriod() int64 { return 0 } -func (m *Params) GetReputationHalfLifeFactor() int64 { +func (m *Params) GetReputationHalfLifeFactor() uint64 { if m != nil { return m.ReputationHalfLifeFactor } @@ -109,45 +109,45 @@ func init() { func init() { proto.RegisterFile("lavanet/lava/pairing/params.proto", fileDescriptor_fc338fce33b3b67a) } var fileDescriptor_fc338fce33b3b67a = []byte{ - // 607 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x41, 0x6f, 0xd3, 0x30, - 0x14, 0xc7, 0x9b, 0xcd, 0xeb, 0xbc, 0x6c, 0x83, 0x10, 0xed, 0x10, 0x6d, 0x22, 0x19, 0x01, 0xa6, - 0x6a, 0x12, 0xcd, 0x61, 0x9c, 0x76, 0xec, 0xc6, 0x84, 0xa2, 0x89, 0x95, 0x0c, 0x81, 0x84, 0x90, - 0x2c, 0xd7, 0x75, 0x5b, 0x6b, 0x4e, 0x1c, 0x39, 0x6e, 0x21, 0xdc, 0xb9, 0x71, 0xe0, 0xc8, 0x91, - 0x2b, 0x7c, 0x92, 0x1d, 0x77, 0x44, 0x1c, 0x22, 0xb4, 0x7e, 0x83, 0x7e, 0x02, 0x54, 0xa7, 0xac, - 0x1d, 0x05, 0x69, 0x9c, 0x5e, 0x94, 0xf7, 0xfb, 0xbf, 0xff, 0xb3, 0x9f, 0x6d, 0xf3, 0x1e, 0xc7, - 0x03, 0x9c, 0x50, 0x15, 0x8c, 0x63, 0x90, 0x62, 0x26, 0x59, 0xd2, 0x0d, 0x52, 0x2c, 0x71, 0x9c, - 0xd5, 0x53, 0x29, 0x94, 0xb0, 0x37, 0x26, 0x48, 0x7d, 0x1c, 0xeb, 0x13, 0x64, 0x73, 0xa3, 0x2b, - 0xba, 0x42, 0x03, 0xc1, 0xf8, 0xab, 0x64, 0xfd, 0xaf, 0xcb, 0x66, 0xb5, 0xa9, 0xc5, 0xf6, 0x89, - 0x69, 0xd3, 0x54, 0x90, 0x5e, 0x83, 0x0b, 0x72, 0x96, 0x9d, 0x0c, 0xa8, 0xe4, 0x38, 0x75, 0xe0, - 0xb6, 0x51, 0x03, 0x0d, 0x6f, 0x54, 0x78, 0x5b, 0x39, 0x8e, 0xf9, 0xbe, 0xaf, 0x19, 0xd4, 0xd2, - 0x10, 0x12, 0x25, 0xe5, 0x47, 0x7f, 0x91, 0xda, 0x89, 0xb9, 0xf2, 0x5c, 0x9c, 0xbe, 0xa2, 0xac, - 0xdb, 0x53, 0xce, 0xfa, 0xb6, 0x51, 0x5b, 0x69, 0x34, 0xcf, 0x0b, 0xaf, 0xf2, 0xa3, 0xf0, 0x76, - 0xba, 0x4c, 0xf5, 0xfa, 0xad, 0x3a, 0x11, 0x71, 0x40, 0x44, 0x16, 0x8b, 0x6c, 0x12, 0x1e, 0x65, - 0xed, 0xb3, 0x40, 0xe5, 0x29, 0xcd, 0xea, 0x87, 0x94, 0x8c, 0x0a, 0xcf, 0x2d, 0x5d, 0xdb, 0x58, - 0x61, 0x24, 0x29, 0x67, 0xb8, 0xc5, 0x38, 0x53, 0x39, 0x92, 0xf4, 0x2d, 0x96, 0x6d, 0x3f, 0x9a, - 0x5a, 0xd8, 0x1f, 0x0c, 0xf3, 0xbe, 0xa4, 0x44, 0xc4, 0x31, 0x4d, 0xda, 0xb4, 0xfd, 0x64, 0xdc, - 0xd1, 0xb3, 0x7e, 0xfc, 0x42, 0x1c, 0x08, 0xce, 0x29, 0x51, 0x4d, 0x9c, 0xc7, 0x34, 0x51, 0xce, - 0x2d, 0xbd, 0xa4, 0xbd, 0x51, 0xe1, 0x05, 0x65, 0xf1, 0x19, 0x11, 0x2a, 0x97, 0x97, 0xf4, 0x63, - 0xa4, 0x04, 0x22, 0xa5, 0x10, 0xa5, 0xa5, 0xd2, 0x8f, 0x6e, 0x52, 0xdf, 0xfe, 0x68, 0x98, 0x35, - 0x49, 0xd3, 0xbe, 0xc2, 0x8a, 0x89, 0x04, 0x0d, 0xb0, 0x64, 0x38, 0x21, 0x14, 0x65, 0x4a, 0x37, - 0xff, 0xbe, 0xfc, 0x9d, 0x52, 0xc9, 0x44, 0xdb, 0xb9, 0xbd, 0x6d, 0xd4, 0x16, 0xaf, 0x37, 0x73, - 0x33, 0xa5, 0x1f, 0x3d, 0x9c, 0xa2, 0x2f, 0x27, 0xe4, 0xe9, 0x2c, 0xd8, 0xd4, 0x9c, 0xfd, 0x4d, - 0x6f, 0xcb, 0x55, 0x51, 0x8e, 0x15, 0x4d, 0x48, 0xae, 0x47, 0x87, 0xb2, 0x3c, 0x21, 0xa8, 0x83, - 0x89, 0x12, 0xd2, 0xb1, 0xf4, 0x84, 0xde, 0xfc, 0xf7, 0x84, 0x76, 0xe7, 0xfa, 0xfe, 0x97, 0x85, - 0x1f, 0x79, 0x53, 0xea, 0xb8, 0x84, 0xc6, 0x87, 0xe5, 0x34, 0x4f, 0xc8, 0x91, 0x26, 0x6c, 0x6a, - 0x6e, 0xcd, 0x14, 0xea, 0x61, 0xde, 0x41, 0x9c, 0x75, 0xe8, 0xef, 0x1e, 0xef, 0xe8, 0xdd, 0xda, - 0x19, 0x15, 0x9e, 0x3f, 0xe7, 0xfa, 0x27, 0xec, 0x47, 0xce, 0x34, 0xfb, 0x14, 0xf3, 0xce, 0x31, - 0xeb, 0xd0, 0x89, 0xcd, 0x99, 0x79, 0x77, 0x46, 0x29, 0x29, 0xc7, 0x39, 0xea, 0x60, 0xc6, 0xfb, - 0x92, 0x22, 0x22, 0x32, 0xe5, 0xd8, 0xfa, 0x8c, 0xd4, 0x46, 0x85, 0xf7, 0x60, 0xce, 0x68, 0x1e, - 0xf7, 0xa3, 0xcd, 0x69, 0x3e, 0x1a, 0xa7, 0x8f, 0xca, 0xec, 0x81, 0xc8, 0xd4, 0x3e, 0xf8, 0xfc, - 0xc5, 0xab, 0x84, 0x00, 0x1a, 0xd6, 0x42, 0x08, 0xe0, 0x82, 0xb5, 0x18, 0x02, 0xb8, 0x68, 0x81, - 0x10, 0x40, 0x60, 0x2d, 0x85, 0x00, 0x2e, 0x59, 0xd5, 0x10, 0xc0, 0xaa, 0xb5, 0x1c, 0x02, 0xb8, - 0x6c, 0xc1, 0x10, 0xc0, 0x15, 0xcb, 0x0c, 0x01, 0x34, 0xad, 0xd5, 0x10, 0xc0, 0x55, 0x6b, 0x2d, - 0x04, 0x70, 0xcd, 0x5a, 0x6f, 0x1c, 0x9e, 0x5f, 0xba, 0xc6, 0xc5, 0xa5, 0x6b, 0xfc, 0xbc, 0x74, - 0x8d, 0x4f, 0x43, 0xb7, 0x72, 0x31, 0x74, 0x2b, 0xdf, 0x87, 0x6e, 0xe5, 0xf5, 0xee, 0xcc, 0xb0, - 0xae, 0xbd, 0x0f, 0x83, 0xc7, 0xc1, 0xbb, 0xab, 0x47, 0x42, 0x0f, 0xad, 0x55, 0xd5, 0x17, 0x7f, - 0xef, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x47, 0xb1, 0x29, 0x49, 0x04, 0x00, 0x00, + // 606 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x31, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0xe3, 0xf6, 0x9a, 0x5e, 0xdd, 0x16, 0x8c, 0xd5, 0xc1, 0x6a, 0x85, 0x5d, 0x0c, 0x54, + 0x51, 0x25, 0xe2, 0xa1, 0x4c, 0x1d, 0xd3, 0x52, 0x21, 0xab, 0xa2, 0xc1, 0x45, 0x20, 0x21, 0xa4, + 0xd3, 0xe5, 0x72, 0x49, 0x4e, 0x3d, 0xfb, 0xac, 0xf3, 0x25, 0x60, 0x76, 0x36, 0x06, 0x46, 0x46, + 0x56, 0xf8, 0x24, 0x1d, 0x3b, 0x22, 0x06, 0x0b, 0x35, 0xdf, 0x20, 0x9f, 0x00, 0xe5, 0x1c, 0x9a, + 0x94, 0x80, 0x54, 0xa6, 0x17, 0xe5, 0xfd, 0xfe, 0xef, 0xff, 0xce, 0x7f, 0xfb, 0xcc, 0x7b, 0x1c, + 0x0f, 0x70, 0x42, 0x55, 0x30, 0xae, 0x41, 0x8a, 0x99, 0x64, 0x49, 0x37, 0x48, 0xb1, 0xc4, 0x71, + 0x56, 0x4f, 0xa5, 0x50, 0xc2, 0xde, 0x98, 0x20, 0xf5, 0x71, 0xad, 0x4f, 0x90, 0xcd, 0x8d, 0xae, + 0xe8, 0x0a, 0x0d, 0x04, 0xe3, 0x5f, 0x25, 0xeb, 0x7f, 0x5d, 0x36, 0xab, 0x4d, 0x2d, 0xb6, 0x4f, + 0x4c, 0x9b, 0xa6, 0x82, 0xf4, 0x1a, 0x5c, 0x90, 0xb3, 0xec, 0x64, 0x40, 0x25, 0xc7, 0xa9, 0x03, + 0xb7, 0x8d, 0x1a, 0x68, 0x78, 0xa3, 0xc2, 0xdb, 0xca, 0x71, 0xcc, 0xf7, 0x7d, 0xcd, 0xa0, 0x96, + 0x86, 0x90, 0x28, 0x29, 0x3f, 0xfa, 0x8b, 0xd4, 0x4e, 0xcc, 0x95, 0xe7, 0xe2, 0xf4, 0x15, 0x65, + 0xdd, 0x9e, 0x72, 0xd6, 0xb7, 0x8d, 0xda, 0x4a, 0xa3, 0x79, 0x5e, 0x78, 0x95, 0x1f, 0x85, 0xb7, + 0xd3, 0x65, 0xaa, 0xd7, 0x6f, 0xd5, 0x89, 0x88, 0x03, 0x22, 0xb2, 0x58, 0x64, 0x93, 0xf2, 0x28, + 0x6b, 0x9f, 0x05, 0x2a, 0x4f, 0x69, 0x56, 0x3f, 0xa4, 0x64, 0x54, 0x78, 0x6e, 0xe9, 0xda, 0xc6, + 0x0a, 0x23, 0x49, 0x39, 0xc3, 0x2d, 0xc6, 0x99, 0xca, 0x91, 0xa4, 0x6f, 0xb1, 0x6c, 0xfb, 0xd1, + 0xd4, 0xc2, 0xfe, 0x60, 0x98, 0xf7, 0x25, 0x25, 0x22, 0x8e, 0x69, 0xd2, 0xa6, 0xed, 0x27, 0xe3, + 0x8d, 0x9e, 0xf5, 0xe3, 0x17, 0xe2, 0x40, 0x70, 0x4e, 0x89, 0x6a, 0xe2, 0x3c, 0xa6, 0x89, 0x72, + 0x6e, 0xe9, 0x23, 0xed, 0x8d, 0x0a, 0x2f, 0x28, 0x87, 0xcf, 0x88, 0x50, 0x79, 0xbc, 0xa4, 0x1f, + 0x23, 0x25, 0x10, 0x29, 0x85, 0x28, 0x2d, 0x95, 0x7e, 0x74, 0x93, 0xf9, 0xf6, 0x47, 0xc3, 0xac, + 0x49, 0x9a, 0xf6, 0x15, 0x56, 0x4c, 0x24, 0x68, 0x80, 0x25, 0xc3, 0x09, 0xa1, 0x28, 0x53, 0x7a, + 0xf9, 0xf7, 0xe5, 0xdf, 0x29, 0x95, 0x4c, 0xb4, 0x9d, 0xdb, 0xdb, 0x46, 0x6d, 0xf1, 0xfa, 0x32, + 0x37, 0x53, 0xfa, 0xd1, 0xc3, 0x29, 0xfa, 0x72, 0x42, 0x9e, 0xce, 0x82, 0x4d, 0xcd, 0xd9, 0xdf, + 0xf4, 0x63, 0xb9, 0x1a, 0xca, 0xb1, 0xa2, 0x09, 0xc9, 0x75, 0x74, 0x28, 0xcb, 0x13, 0x82, 0x3a, + 0x98, 0x28, 0x21, 0x1d, 0x4b, 0x27, 0xf4, 0xe6, 0xbf, 0x13, 0xda, 0x9d, 0xdb, 0xfb, 0x5f, 0x16, + 0x7e, 0xe4, 0x4d, 0xa9, 0xe3, 0x12, 0x1a, 0xbf, 0x2c, 0xa7, 0x79, 0x42, 0x8e, 0x34, 0x61, 0x53, + 0x73, 0x6b, 0x66, 0x50, 0x0f, 0xf3, 0x0e, 0xe2, 0xac, 0x43, 0x7f, 0xef, 0x78, 0x47, 0x47, 0xb7, + 0x33, 0x2a, 0x3c, 0x7f, 0xce, 0xf5, 0x4f, 0xd8, 0x8f, 0x9c, 0x69, 0xf7, 0x29, 0xe6, 0x9d, 0x63, + 0xd6, 0xa1, 0x13, 0x9b, 0x33, 0xf3, 0xee, 0x8c, 0x52, 0x52, 0x8e, 0x73, 0xd4, 0xc1, 0x8c, 0xf7, + 0x25, 0x45, 0x44, 0x64, 0xca, 0xb1, 0xb5, 0x51, 0x6d, 0x54, 0x78, 0x0f, 0xe6, 0x8c, 0xe6, 0x71, + 0x3f, 0xda, 0x9c, 0xf6, 0xa3, 0x71, 0xfb, 0xa8, 0xec, 0x1e, 0x88, 0x4c, 0xed, 0x83, 0xcf, 0x5f, + 0xbc, 0x4a, 0x08, 0xa0, 0x61, 0x2d, 0x84, 0x00, 0x2e, 0x58, 0x8b, 0x21, 0x80, 0x8b, 0x16, 0x08, + 0x01, 0x04, 0xd6, 0x52, 0x08, 0xe0, 0x92, 0x55, 0x0d, 0x01, 0xac, 0x5a, 0xcb, 0x21, 0x80, 0xcb, + 0x16, 0x0c, 0x01, 0x5c, 0xb1, 0xcc, 0x10, 0x40, 0xd3, 0x5a, 0x0d, 0x01, 0x5c, 0xb5, 0xd6, 0x42, + 0x00, 0xd7, 0xac, 0xf5, 0xc6, 0xe1, 0xf9, 0xa5, 0x6b, 0x5c, 0x5c, 0xba, 0xc6, 0xcf, 0x4b, 0xd7, + 0xf8, 0x34, 0x74, 0x2b, 0x17, 0x43, 0xb7, 0xf2, 0x7d, 0xe8, 0x56, 0x5e, 0xef, 0xce, 0x84, 0x75, + 0xed, 0x7e, 0x18, 0x3c, 0x0e, 0xde, 0x5d, 0x5d, 0x12, 0x3a, 0xb4, 0x56, 0x55, 0x7f, 0xf8, 0x7b, + 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x90, 0x7b, 0x61, 0x25, 0x49, 0x04, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -437,7 +437,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ReputationHalfLifeFactor |= int64(b&0x7F) << shift + m.ReputationHalfLifeFactor |= uint64(b&0x7F) << shift if b < 0x80 { break } diff --git a/x/pairing/types/qos_score.go b/x/pairing/types/qos_score.go new file mode 100644 index 0000000000..443e1eba6a --- /dev/null +++ b/x/pairing/types/qos_score.go @@ -0,0 +1,88 @@ +package types + +import ( + "fmt" + + "cosmossdk.io/math" + "github.com/lavanet/lava/v4/utils" +) + +var ( + FailureCost int64 = 3000 // failed relay cost for QoS excellence report computation in milliseconds + TruncateStdMargin int64 = 3 // number of standard deviations that determine the truncation limit + + // zero QoS score is: score = 0, var = 0 + ZeroQosScore = QosScore{ + Score: Frac{Num: math.LegacyZeroDec(), Denom: math.LegacySmallestDec()}, + Variance: Frac{Num: math.LegacyZeroDec(), Denom: math.LegacySmallestDec()}, + } +) + +func NewFrac(num math.LegacyDec, denom math.LegacyDec) (Frac, error) { + if denom.Equal(math.LegacyZeroDec()) { + return Frac{}, fmt.Errorf("cannot create Frac with zero denomination") + } + return Frac{Num: num, Denom: denom}, nil +} + +func (f Frac) Equal(other Frac) bool { + return f.Num.Equal(other.Num) && f.Denom.Equal(other.Denom) +} + +func (f Frac) Resolve() (math.LegacyDec, error) { + if f.Denom.IsZero() { + return math.LegacyZeroDec(), utils.LavaFormatError("Frac Resolve: resolve failed", fmt.Errorf("frac has zero denom")) + } + + return f.Num.Quo(f.Denom), nil +} + +func NewQosScore(score Frac, variance Frac) QosScore { + return QosScore{Score: score, Variance: variance} +} + +func (qs QosScore) Equal(other QosScore) bool { + return qs.Score.Equal(other.Score) && qs.Variance.Equal(other.Variance) +} + +// Validate validates that both nums are non-negative and both denoms are strictly positive (non-zero) +func (qs QosScore) Validate() bool { + if qs.Score.Num.IsNegative() || !qs.Score.Denom.IsPositive() || qs.Variance.Num.IsNegative() || + !qs.Variance.Denom.IsPositive() { + return false + } + return true +} + +// Update updates a QosScore with a new score from the QoS excellence report. The new score is truncated by the +// current variance. Then, it's updated using the weight (which is currently the relay num) +func (qs *QosScore) Update(score math.LegacyDec, truncate bool, weight int64) { + if truncate { + score = qs.truncate(score) + } + + // updated_variance_num = qos_variance_num + (qos_score_num - score)^2 * weight + // updated_score_denom = qos_score_denom + weight + qs.Variance.Num = qs.Variance.Num.Add((qs.Score.Num.Sub(score)).Power(2).MulInt64(weight)) + qs.Variance.Denom = qs.Variance.Denom.Add(math.LegacyNewDec(weight)) + + // updated_score_num = qos_score_num + score * weight + // updated_score_denom = qos_score_denom + weight + qs.Score.Num = qs.Score.Num.Add(score.MulInt64(weight)) + qs.Score.Denom = qs.Score.Denom.Add(math.LegacyNewDec(weight)) +} + +// Truncate truncates the QoS excellece report score by the current QoS score variance +func (qs QosScore) truncate(score math.LegacyDec) math.LegacyDec { + std, err := qs.Variance.Num.ApproxSqrt() + if err != nil { + utils.LavaFormatError("QosScore truncate: truncate failed, could not calculate qos variance sqrt", err, + utils.LogAttr("qos_score_variance", qs.Variance.String()), + ) + return score + } + + // truncated score = max(min(score, qos_score_num + 3*std), qos_score_num - 3*std) + return math.LegacyMaxDec(math.LegacyMinDec(score, qs.Score.Num.Add(std.MulInt64(TruncateStdMargin))), + qs.Score.Num.Sub(std.MulInt64(TruncateStdMargin))) +} diff --git a/x/pairing/types/query.pb.go b/x/pairing/types/query.pb.go index f69bba8484..a8e958f3d0 100644 --- a/x/pairing/types/query.pb.go +++ b/x/pairing/types/query.pb.go @@ -1543,6 +1543,360 @@ func (m *ProviderCuInfo) GetCu() uint64 { return 0 } +type QueryProviderReputationRequest struct { + Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` + ChainID string `protobuf:"bytes,2,opt,name=chainID,proto3" json:"chainID,omitempty"` + Cluster string `protobuf:"bytes,3,opt,name=cluster,proto3" json:"cluster,omitempty"` +} + +func (m *QueryProviderReputationRequest) Reset() { *m = QueryProviderReputationRequest{} } +func (m *QueryProviderReputationRequest) String() string { return proto.CompactTextString(m) } +func (*QueryProviderReputationRequest) ProtoMessage() {} +func (*QueryProviderReputationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9e149ce9d21da0d8, []int{29} +} +func (m *QueryProviderReputationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProviderReputationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProviderReputationRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProviderReputationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProviderReputationRequest.Merge(m, src) +} +func (m *QueryProviderReputationRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryProviderReputationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProviderReputationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProviderReputationRequest proto.InternalMessageInfo + +func (m *QueryProviderReputationRequest) GetProvider() string { + if m != nil { + return m.Provider + } + return "" +} + +func (m *QueryProviderReputationRequest) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +func (m *QueryProviderReputationRequest) GetCluster() string { + if m != nil { + return m.Cluster + } + return "" +} + +type ReputationData struct { + Rank uint64 `protobuf:"varint,1,opt,name=rank,proto3" json:"rank,omitempty"` + Providers uint64 `protobuf:"varint,2,opt,name=providers,proto3" json:"providers,omitempty"` + OverallPerformance string `protobuf:"bytes,3,opt,name=overall_performance,json=overallPerformance,proto3" json:"overall_performance,omitempty"` + ChainID string `protobuf:"bytes,4,opt,name=chainID,proto3" json:"chainID,omitempty"` + Cluster string `protobuf:"bytes,5,opt,name=cluster,proto3" json:"cluster,omitempty"` +} + +func (m *ReputationData) Reset() { *m = ReputationData{} } +func (m *ReputationData) String() string { return proto.CompactTextString(m) } +func (*ReputationData) ProtoMessage() {} +func (*ReputationData) Descriptor() ([]byte, []int) { + return fileDescriptor_9e149ce9d21da0d8, []int{30} +} +func (m *ReputationData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReputationData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReputationData.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReputationData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReputationData.Merge(m, src) +} +func (m *ReputationData) XXX_Size() int { + return m.Size() +} +func (m *ReputationData) XXX_DiscardUnknown() { + xxx_messageInfo_ReputationData.DiscardUnknown(m) +} + +var xxx_messageInfo_ReputationData proto.InternalMessageInfo + +func (m *ReputationData) GetRank() uint64 { + if m != nil { + return m.Rank + } + return 0 +} + +func (m *ReputationData) GetProviders() uint64 { + if m != nil { + return m.Providers + } + return 0 +} + +func (m *ReputationData) GetOverallPerformance() string { + if m != nil { + return m.OverallPerformance + } + return "" +} + +func (m *ReputationData) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +func (m *ReputationData) GetCluster() string { + if m != nil { + return m.Cluster + } + return "" +} + +type QueryProviderReputationResponse struct { + Data []ReputationData `protobuf:"bytes,1,rep,name=data,proto3" json:"data"` +} + +func (m *QueryProviderReputationResponse) Reset() { *m = QueryProviderReputationResponse{} } +func (m *QueryProviderReputationResponse) String() string { return proto.CompactTextString(m) } +func (*QueryProviderReputationResponse) ProtoMessage() {} +func (*QueryProviderReputationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9e149ce9d21da0d8, []int{31} +} +func (m *QueryProviderReputationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProviderReputationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProviderReputationResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProviderReputationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProviderReputationResponse.Merge(m, src) +} +func (m *QueryProviderReputationResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryProviderReputationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProviderReputationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProviderReputationResponse proto.InternalMessageInfo + +func (m *QueryProviderReputationResponse) GetData() []ReputationData { + if m != nil { + return m.Data + } + return nil +} + +type QueryProviderReputationDetailsRequest struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + ChainID string `protobuf:"bytes,2,opt,name=chainID,proto3" json:"chainID,omitempty"` + Cluster string `protobuf:"bytes,3,opt,name=cluster,proto3" json:"cluster,omitempty"` +} + +func (m *QueryProviderReputationDetailsRequest) Reset() { *m = QueryProviderReputationDetailsRequest{} } +func (m *QueryProviderReputationDetailsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryProviderReputationDetailsRequest) ProtoMessage() {} +func (*QueryProviderReputationDetailsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9e149ce9d21da0d8, []int{32} +} +func (m *QueryProviderReputationDetailsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProviderReputationDetailsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProviderReputationDetailsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProviderReputationDetailsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProviderReputationDetailsRequest.Merge(m, src) +} +func (m *QueryProviderReputationDetailsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryProviderReputationDetailsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProviderReputationDetailsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProviderReputationDetailsRequest proto.InternalMessageInfo + +func (m *QueryProviderReputationDetailsRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *QueryProviderReputationDetailsRequest) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +func (m *QueryProviderReputationDetailsRequest) GetCluster() string { + if m != nil { + return m.Cluster + } + return "" +} + +type ReputationDevData struct { + Reputation Reputation `protobuf:"bytes,1,opt,name=reputation,proto3" json:"reputation"` + ReputationPairingScore ReputationPairingScore `protobuf:"bytes,2,opt,name=reputation_pairing_score,json=reputationPairingScore,proto3" json:"reputation_pairing_score"` + ChainID string `protobuf:"bytes,4,opt,name=chainID,proto3" json:"chainID,omitempty"` + Cluster string `protobuf:"bytes,5,opt,name=cluster,proto3" json:"cluster,omitempty"` +} + +func (m *ReputationDevData) Reset() { *m = ReputationDevData{} } +func (m *ReputationDevData) String() string { return proto.CompactTextString(m) } +func (*ReputationDevData) ProtoMessage() {} +func (*ReputationDevData) Descriptor() ([]byte, []int) { + return fileDescriptor_9e149ce9d21da0d8, []int{33} +} +func (m *ReputationDevData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReputationDevData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReputationDevData.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReputationDevData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReputationDevData.Merge(m, src) +} +func (m *ReputationDevData) XXX_Size() int { + return m.Size() +} +func (m *ReputationDevData) XXX_DiscardUnknown() { + xxx_messageInfo_ReputationDevData.DiscardUnknown(m) +} + +var xxx_messageInfo_ReputationDevData proto.InternalMessageInfo + +func (m *ReputationDevData) GetReputation() Reputation { + if m != nil { + return m.Reputation + } + return Reputation{} +} + +func (m *ReputationDevData) GetReputationPairingScore() ReputationPairingScore { + if m != nil { + return m.ReputationPairingScore + } + return ReputationPairingScore{} +} + +func (m *ReputationDevData) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +func (m *ReputationDevData) GetCluster() string { + if m != nil { + return m.Cluster + } + return "" +} + +type QueryProviderReputationDetailsResponse struct { + Data []ReputationDevData `protobuf:"bytes,1,rep,name=data,proto3" json:"data"` +} + +func (m *QueryProviderReputationDetailsResponse) Reset() { + *m = QueryProviderReputationDetailsResponse{} +} +func (m *QueryProviderReputationDetailsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryProviderReputationDetailsResponse) ProtoMessage() {} +func (*QueryProviderReputationDetailsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9e149ce9d21da0d8, []int{34} +} +func (m *QueryProviderReputationDetailsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProviderReputationDetailsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProviderReputationDetailsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProviderReputationDetailsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProviderReputationDetailsResponse.Merge(m, src) +} +func (m *QueryProviderReputationDetailsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryProviderReputationDetailsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProviderReputationDetailsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProviderReputationDetailsResponse proto.InternalMessageInfo + +func (m *QueryProviderReputationDetailsResponse) GetData() []ReputationDevData { + if m != nil { + return m.Data + } + return nil +} + func init() { proto.RegisterType((*QueryParamsRequest)(nil), "lavanet.lava.pairing.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "lavanet.lava.pairing.QueryParamsResponse") @@ -1573,125 +1927,148 @@ func init() { proto.RegisterType((*QueryProvidersEpochCuRequest)(nil), "lavanet.lava.pairing.QueryProvidersEpochCuRequest") proto.RegisterType((*QueryProvidersEpochCuResponse)(nil), "lavanet.lava.pairing.QueryProvidersEpochCuResponse") proto.RegisterType((*ProviderCuInfo)(nil), "lavanet.lava.pairing.ProviderCuInfo") + proto.RegisterType((*QueryProviderReputationRequest)(nil), "lavanet.lava.pairing.QueryProviderReputationRequest") + proto.RegisterType((*ReputationData)(nil), "lavanet.lava.pairing.ReputationData") + proto.RegisterType((*QueryProviderReputationResponse)(nil), "lavanet.lava.pairing.QueryProviderReputationResponse") + proto.RegisterType((*QueryProviderReputationDetailsRequest)(nil), "lavanet.lava.pairing.QueryProviderReputationDetailsRequest") + proto.RegisterType((*ReputationDevData)(nil), "lavanet.lava.pairing.ReputationDevData") + proto.RegisterType((*QueryProviderReputationDetailsResponse)(nil), "lavanet.lava.pairing.QueryProviderReputationDetailsResponse") } func init() { proto.RegisterFile("lavanet/lava/pairing/query.proto", fileDescriptor_9e149ce9d21da0d8) } var fileDescriptor_9e149ce9d21da0d8 = []byte{ - // 1807 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x4f, 0x6f, 0xdc, 0xc6, - 0x15, 0x17, 0x57, 0xab, 0xb5, 0xf4, 0xf4, 0xc7, 0xc6, 0x44, 0xb2, 0xa5, 0xad, 0xb2, 0x5a, 0x53, - 0x49, 0xaa, 0x3f, 0xce, 0x32, 0xda, 0xd8, 0x89, 0xa1, 0x28, 0x6e, 0x2b, 0xc9, 0x31, 0xa4, 0xaa, - 0x8d, 0x44, 0xd5, 0x3d, 0x04, 0x05, 0x08, 0x8a, 0x9c, 0x5d, 0x31, 0xe2, 0x72, 0x18, 0x72, 0xb8, - 0x96, 0x2a, 0x6c, 0x0b, 0xb4, 0x5f, 0xa0, 0x40, 0xdb, 0x43, 0x4f, 0xbd, 0x04, 0xe8, 0x29, 0xdf, - 0xa0, 0xb7, 0x02, 0x45, 0x0e, 0x3d, 0x18, 0xe8, 0xa5, 0x28, 0x8a, 0xa0, 0xb0, 0x7b, 0x2e, 0xd0, - 0x6f, 0x50, 0x70, 0x66, 0xc8, 0x25, 0x57, 0x5c, 0xee, 0xca, 0xf1, 0x45, 0xda, 0x19, 0xbe, 0xdf, - 0xcc, 0xef, 0xbd, 0x79, 0xef, 0xcd, 0x8f, 0x84, 0xaa, 0xad, 0xb7, 0x75, 0x07, 0x53, 0x25, 0xfc, - 0xaf, 0xb8, 0xba, 0xe5, 0x59, 0x4e, 0x53, 0xf9, 0x22, 0xc0, 0xde, 0x45, 0xcd, 0xf5, 0x08, 0x25, - 0x68, 0x56, 0x58, 0xd4, 0xc2, 0xff, 0x35, 0x61, 0x51, 0x9e, 0x6d, 0x92, 0x26, 0x61, 0x06, 0x4a, - 0xf8, 0x8b, 0xdb, 0x96, 0x17, 0x9b, 0x84, 0x34, 0x6d, 0xac, 0xe8, 0xae, 0xa5, 0xe8, 0x8e, 0x43, - 0xa8, 0x4e, 0x2d, 0xe2, 0xf8, 0xe2, 0xe9, 0x9a, 0x41, 0xfc, 0x16, 0xf1, 0x95, 0x13, 0xdd, 0xc7, - 0x7c, 0x0b, 0xa5, 0xbd, 0x71, 0x82, 0xa9, 0xbe, 0xa1, 0xb8, 0x7a, 0xd3, 0x72, 0x98, 0xb1, 0xb0, - 0xbd, 0x9b, 0xc9, 0xcb, 0xd5, 0x3d, 0xbd, 0x15, 0x2d, 0xb7, 0x98, 0x32, 0xf1, 0x5d, 0x6c, 0xb0, - 0x3f, 0xe2, 0xe9, 0x52, 0x7a, 0x01, 0x5b, 0x77, 0x7c, 0xc5, 0x25, 0xb6, 0x65, 0x08, 0xbf, 0xca, - 0xeb, 0x29, 0x03, 0xec, 0x12, 0xe3, 0xd4, 0xa7, 0xc4, 0xd3, 0x9b, 0x58, 0xf1, 0xa9, 0x7e, 0x86, - 0x35, 0xec, 0xd0, 0x28, 0x08, 0xe5, 0x7b, 0xe9, 0xbd, 0x82, 0x13, 0xdf, 0xf0, 0x2c, 0x37, 0xe4, - 0x9b, 0x1a, 0x08, 0xeb, 0xe5, 0xf4, 0xde, 0x1e, 0xf9, 0x1c, 0x1b, 0xd4, 0x8f, 0x7e, 0x08, 0xa3, - 0xef, 0xa6, 0x8c, 0x4c, 0xf2, 0xcc, 0xa1, 0x56, 0x0b, 0x2b, 0xed, 0x8d, 0xf8, 0x37, 0x37, 0x94, - 0x67, 0x01, 0x1d, 0x85, 0xc1, 0x3a, 0x64, 0xce, 0xab, 0xf8, 0x8b, 0x00, 0xfb, 0x54, 0x3e, 0x82, - 0x37, 0x52, 0xb3, 0xbe, 0x4b, 0x1c, 0x1f, 0xa3, 0x4d, 0x28, 0xf1, 0x20, 0xcd, 0x4b, 0x55, 0x69, - 0x65, 0xb2, 0xbe, 0x58, 0xcb, 0x3a, 0xbe, 0x1a, 0x47, 0x6d, 0x17, 0xbf, 0xfe, 0x66, 0x69, 0x44, - 0x15, 0x08, 0xf9, 0x08, 0xe6, 0xf8, 0x92, 0x1e, 0x69, 0x5b, 0x26, 0xf6, 0xa2, 0xbd, 0xd0, 0x3c, - 0xdc, 0x30, 0x4e, 0x75, 0xcb, 0xd9, 0xdb, 0x65, 0xab, 0x4e, 0xa8, 0xd1, 0x10, 0x55, 0x00, 0xfc, - 0x53, 0xf2, 0xec, 0x13, 0x8f, 0xfc, 0x1c, 0x3b, 0xf3, 0x85, 0xaa, 0xb4, 0x32, 0xae, 0x26, 0x66, - 0xe4, 0x33, 0xb8, 0xdd, 0xbb, 0xa4, 0x20, 0xfa, 0x43, 0x00, 0x16, 0xe6, 0xc7, 0x61, 0x94, 0xe7, - 0xa5, 0xea, 0xe8, 0xca, 0x64, 0xfd, 0xed, 0x34, 0xd9, 0xe4, 0x99, 0xd4, 0x8e, 0x63, 0x63, 0xc1, - 0x3a, 0x01, 0xdf, 0x2f, 0x8e, 0x17, 0x6e, 0x8d, 0xca, 0xfb, 0x30, 0x9b, 0xda, 0x2c, 0x41, 0x5f, - 0x37, 0x4d, 0x0f, 0xfb, 0x7e, 0x44, 0x5f, 0x0c, 0x93, 0x8e, 0x15, 0x52, 0x8e, 0xc9, 0xa7, 0x3d, - 0xb1, 0x88, 0x79, 0x7f, 0x0a, 0x53, 0xf1, 0xc6, 0x16, 0xf6, 0x5f, 0x85, 0x79, 0x6a, 0x01, 0x79, - 0x5f, 0x84, 0xe8, 0x09, 0xa6, 0x87, 0xfc, 0x74, 0x06, 0x87, 0xfd, 0x36, 0x94, 0x0c, 0xdb, 0xc2, - 0x0e, 0x15, 0xb4, 0xc5, 0x48, 0xfe, 0xaa, 0x00, 0x77, 0xae, 0x2c, 0x26, 0x88, 0xef, 0xc1, 0x84, - 0x1b, 0x9d, 0xc2, 0xab, 0xb0, 0xee, 0xa2, 0xd1, 0x32, 0x4c, 0x1b, 0x81, 0xe7, 0x61, 0x87, 0x6a, - 0x0c, 0xc3, 0x58, 0x14, 0xd5, 0x29, 0x31, 0xf9, 0x38, 0x9c, 0x43, 0x0f, 0x61, 0x21, 0x4c, 0x62, - 0xcd, 0xc6, 0x0d, 0xaa, 0x51, 0xa2, 0x39, 0xf8, 0x9c, 0x6a, 0x22, 0xff, 0xe6, 0x47, 0x19, 0x60, - 0x2e, 0x34, 0x38, 0xc0, 0x0d, 0xfa, 0x13, 0xf2, 0x63, 0x7c, 0x1e, 0x31, 0x46, 0x0f, 0xe0, 0x4e, - 0x58, 0xc8, 0x9a, 0xad, 0xfb, 0x54, 0x0b, 0x5c, 0x53, 0xa7, 0xd8, 0xd4, 0x4e, 0x6c, 0x62, 0x9c, - 0xcd, 0x17, 0x19, 0x6e, 0x36, 0x7c, 0x7c, 0xa0, 0xfb, 0xf4, 0x29, 0x7f, 0xb8, 0x1d, 0x3e, 0x43, - 0x1b, 0x30, 0xc7, 0x8c, 0x34, 0xd2, 0x48, 0x6f, 0x36, 0xc6, 0x40, 0x88, 0x3d, 0xfc, 0xb4, 0x91, - 0xd8, 0x49, 0xfe, 0x25, 0x2c, 0xb0, 0x70, 0xfd, 0x14, 0x7b, 0x56, 0xe3, 0xe2, 0xdb, 0x86, 0x1f, - 0x95, 0x61, 0x3c, 0x0a, 0x12, 0xf3, 0x70, 0x42, 0x8d, 0xc7, 0x68, 0x16, 0xc6, 0x92, 0x2e, 0xf0, - 0x81, 0xfc, 0xa5, 0x04, 0xe5, 0x2c, 0x06, 0xe2, 0xcc, 0x66, 0x61, 0xac, 0xad, 0xdb, 0x96, 0xc9, - 0x08, 0x8c, 0xab, 0x7c, 0x80, 0x56, 0xe1, 0x56, 0xe8, 0x1a, 0x36, 0xb5, 0xee, 0x81, 0xf2, 0x80, - 0xde, 0xe4, 0xf3, 0x71, 0xb5, 0xa1, 0x2a, 0x4c, 0x19, 0x81, 0xe6, 0x62, 0x4f, 0x1c, 0x14, 0xdf, - 0x1c, 0x8c, 0xe0, 0x10, 0x7b, 0xfc, 0x98, 0xde, 0x04, 0x10, 0x7d, 0x49, 0xb3, 0x4c, 0x16, 0xaa, - 0x09, 0x76, 0xd4, 0xe1, 0xcc, 0x9e, 0x29, 0x2a, 0xeb, 0xf7, 0x12, 0xdc, 0x4d, 0x95, 0x83, 0x20, - 0xba, 0x73, 0xaa, 0x3b, 0x06, 0x8e, 0x02, 0x96, 0x74, 0x5f, 0xea, 0x71, 0xbf, 0x6f, 0xa5, 0xa1, - 0x2a, 0x4c, 0x36, 0x31, 0xb1, 0x89, 0xc1, 0xda, 0x3f, 0x73, 0x64, 0x4c, 0x4d, 0x4e, 0x31, 0xac, - 0x1d, 0xf8, 0x14, 0x7b, 0x8c, 0x7f, 0x88, 0xe5, 0x43, 0xd9, 0x06, 0x39, 0x8f, 0x96, 0x88, 0xe2, - 0x27, 0x50, 0x32, 0xd8, 0x0c, 0x67, 0xb5, 0x5d, 0x0b, 0xf3, 0xf9, 0x9f, 0xdf, 0x2c, 0xbd, 0xd3, - 0xb4, 0xe8, 0x69, 0x70, 0x52, 0x33, 0x48, 0x4b, 0x11, 0x57, 0x13, 0xff, 0xf7, 0xae, 0x6f, 0x9e, - 0x29, 0xf4, 0xc2, 0xc5, 0x7e, 0x6d, 0x17, 0x1b, 0xaa, 0x40, 0xcb, 0xba, 0xe8, 0x09, 0x4f, 0x7d, - 0xec, 0xb1, 0xca, 0xf8, 0x16, 0x0d, 0xa6, 0x9b, 0x0f, 0xa3, 0xc9, 0x7c, 0x78, 0x26, 0x9a, 0x41, - 0x62, 0x0b, 0xe1, 0xc4, 0x13, 0x18, 0x37, 0x88, 0xe3, 0x07, 0x2d, 0x11, 0xdc, 0x6b, 0x56, 0x6f, - 0x0c, 0x0e, 0x37, 0x6e, 0xe9, 0xe7, 0x3b, 0x4f, 0x45, 0xd1, 0xf2, 0x81, 0xfc, 0x11, 0x2c, 0xb1, - 0x8d, 0x8f, 0xc3, 0x2b, 0xdb, 0x88, 0x13, 0xe8, 0xc0, 0xf2, 0xe9, 0xc0, 0x7a, 0x90, 0x5b, 0x50, - 0xed, 0x0f, 0x7e, 0xed, 0xed, 0x47, 0x3e, 0x82, 0xef, 0xb0, 0xed, 0x1e, 0x37, 0x1a, 0xd8, 0xa0, - 0x56, 0x1b, 0x1f, 0xb2, 0x7b, 0x3d, 0x91, 0x86, 0xa9, 0x48, 0x4d, 0x24, 0x9c, 0xbf, 0x0d, 0xa5, - 0xb0, 0x77, 0xc4, 0xc7, 0x21, 0x46, 0x61, 0x82, 0x2f, 0x66, 0xaf, 0x29, 0xe8, 0xd7, 0xa1, 0xc4, - 0xd5, 0x83, 0x08, 0x7e, 0xb9, 0xe7, 0x5e, 0x0d, 0xf5, 0x45, 0x4d, 0x60, 0x84, 0x25, 0xfa, 0x01, - 0xcc, 0xb8, 0xd8, 0x31, 0x2d, 0xa7, 0xa9, 0x09, 0x6c, 0x61, 0x20, 0x76, 0x5a, 0x20, 0xf8, 0x50, - 0xfe, 0x9f, 0x24, 0x1a, 0xfa, 0xb1, 0x79, 0xd6, 0xdb, 0x1c, 0x9e, 0xc0, 0x8d, 0xa8, 0xc3, 0x71, - 0x4e, 0xef, 0x66, 0xdf, 0xf5, 0x7d, 0x2e, 0x04, 0x35, 0x42, 0xa3, 0x39, 0x28, 0xb5, 0xf4, 0x73, - 0xcd, 0x08, 0x92, 0x29, 0x11, 0xa0, 0x75, 0x28, 0x86, 0xd1, 0x61, 0x09, 0x3a, 0x59, 0xbf, 0x93, - 0x5e, 0x9c, 0x29, 0xad, 0x63, 0x17, 0x1b, 0x2a, 0x33, 0x42, 0x7b, 0x70, 0x33, 0x92, 0x2d, 0x9a, - 0x10, 0x20, 0x45, 0x86, 0xab, 0xa6, 0x71, 0xb1, 0xb6, 0x69, 0x6f, 0x08, 0x11, 0xa2, 0xce, 0x44, - 0x73, 0x7c, 0x2c, 0x7f, 0xaf, 0xa7, 0xd7, 0xfc, 0x88, 0x38, 0xf4, 0xd4, 0xbe, 0x38, 0xd4, 0x2f, - 0x48, 0x40, 0x87, 0xe8, 0x35, 0xf2, 0x19, 0xa0, 0xe3, 0x84, 0x28, 0xe3, 0x40, 0x24, 0xc3, 0x54, - 0x52, 0xaa, 0x09, 0x54, 0x6a, 0x0e, 0x2d, 0xc0, 0x38, 0xcb, 0xe9, 0xb0, 0x15, 0xa6, 0xea, 0xd5, - 0x0c, 0x33, 0x47, 0x6f, 0x91, 0xc0, 0xa1, 0xa2, 0x60, 0xc5, 0x48, 0xfe, 0x45, 0x4f, 0x0b, 0xea, - 0x61, 0xdb, 0x6d, 0xe4, 0x94, 0x50, 0xdd, 0x66, 0xbb, 0x16, 0x55, 0x3e, 0x40, 0xdb, 0x70, 0xc3, - 0xc4, 0x54, 0xb7, 0x6c, 0x7f, 0xbe, 0xc0, 0x2a, 0x62, 0x25, 0xfb, 0x04, 0xaf, 0x7a, 0xa3, 0x46, - 0x40, 0x79, 0x17, 0x66, 0xba, 0xdd, 0x8f, 0x39, 0x9a, 0xd7, 0x86, 0xbb, 0x5e, 0x14, 0x52, 0x5e, - 0x7c, 0x0e, 0xd3, 0x3b, 0xbc, 0x98, 0xc5, 0x22, 0xc9, 0x48, 0x48, 0xe9, 0x48, 0x3c, 0x0a, 0xf3, - 0x2e, 0x34, 0x8a, 0x58, 0xbf, 0xd5, 0x47, 0x63, 0xa6, 0x68, 0xa9, 0x11, 0x48, 0xde, 0x81, 0xb7, - 0x79, 0x4a, 0x27, 0xbc, 0xea, 0x77, 0xc6, 0xfd, 0x0a, 0x59, 0xee, 0xc0, 0x3b, 0x83, 0x16, 0xc9, - 0x0d, 0xfd, 0xc7, 0xbd, 0xa1, 0x5f, 0xce, 0x76, 0x22, 0x15, 0x95, 0x6e, 0xd4, 0x2b, 0xa2, 0x5d, - 0xc4, 0xbd, 0x8e, 0x5d, 0xa6, 0x3b, 0x41, 0xa4, 0xce, 0x35, 0x78, 0xb3, 0xcf, 0x73, 0xc1, 0xea, - 0x11, 0x14, 0x2d, 0xa7, 0x41, 0x44, 0x27, 0x1c, 0x10, 0xc1, 0x9d, 0x60, 0xcf, 0x69, 0x10, 0xd1, - 0x08, 0x19, 0x4e, 0xde, 0xea, 0x1e, 0x3b, 0x7f, 0x9a, 0x7b, 0xec, 0x33, 0x50, 0x88, 0xab, 0xbb, - 0x60, 0x04, 0xf5, 0xe7, 0x08, 0xc6, 0x18, 0x3f, 0xf4, 0x6b, 0x09, 0x4a, 0xbc, 0xee, 0xd0, 0x4a, - 0x4e, 0xfb, 0x48, 0xbd, 0x7b, 0x94, 0x57, 0x87, 0xb0, 0xe4, 0x7e, 0xca, 0x6f, 0xfd, 0xea, 0xef, - 0xff, 0xf9, 0x6d, 0xa1, 0x82, 0x16, 0x95, 0x9c, 0x17, 0x3a, 0xf4, 0x07, 0x09, 0x26, 0xba, 0xa2, - 0x65, 0x3d, 0x6f, 0xf9, 0x9e, 0x77, 0x93, 0xf2, 0xbd, 0xe1, 0x8c, 0x05, 0x9d, 0x0d, 0x46, 0x67, - 0x1d, 0xad, 0xf6, 0xa1, 0x13, 0x01, 0x94, 0x4b, 0x71, 0xb7, 0x75, 0xd0, 0x1f, 0x25, 0x18, 0x8f, - 0x16, 0x42, 0x6b, 0x43, 0xec, 0x16, 0x31, 0x5b, 0x1f, 0xca, 0x56, 0x10, 0xdb, 0x64, 0xc4, 0xee, - 0xa3, 0x7a, 0x3e, 0x31, 0xe5, 0x52, 0x28, 0x8b, 0x4e, 0x82, 0xe1, 0x9f, 0x24, 0x80, 0x6e, 0x7f, - 0x47, 0xf7, 0x86, 0xbc, 0x06, 0x38, 0xcb, 0xeb, 0x5d, 0x1a, 0xf2, 0x16, 0xe3, 0xf9, 0x01, 0xba, - 0x9f, 0xcd, 0xb3, 0x89, 0x63, 0xd9, 0xdd, 0x25, 0xa8, 0x5c, 0x72, 0x7d, 0xdc, 0x41, 0x7f, 0x95, - 0x60, 0x3a, 0xa5, 0x74, 0x91, 0x92, 0xb3, 0x7d, 0x96, 0x2a, 0x2f, 0xbf, 0x37, 0x3c, 0x40, 0x50, - 0x56, 0x19, 0xe5, 0x03, 0xb4, 0x9f, 0x4d, 0xb9, 0xcd, 0x40, 0x39, 0xac, 0x95, 0xcb, 0x28, 0xfa, - 0x1d, 0xe5, 0x92, 0xc9, 0xb4, 0x0e, 0xfa, 0xaf, 0x04, 0x73, 0x99, 0xa2, 0x13, 0x7d, 0x38, 0xc4, - 0xa9, 0x67, 0xa9, 0xe7, 0xf2, 0xc3, 0xeb, 0x03, 0x85, 0x83, 0x27, 0xcc, 0xc1, 0x9f, 0xa1, 0xcf, - 0xf2, 0x73, 0x27, 0x72, 0x51, 0xe3, 0x72, 0x36, 0xe5, 0x56, 0xd7, 0xe9, 0x84, 0xf0, 0x66, 0x21, - 0x60, 0x42, 0xbb, 0x83, 0xbe, 0x94, 0x60, 0x22, 0x16, 0xa5, 0xb9, 0x15, 0xda, 0xab, 0x8e, 0x73, - 0x2b, 0xf4, 0x8a, 0xce, 0x1d, 0x94, 0x60, 0x81, 0x1f, 0xbe, 0xcb, 0x84, 0x88, 0xcc, 0x52, 0xf8, - 0x8b, 0x04, 0x6f, 0x64, 0xa8, 0x50, 0xf4, 0x20, 0x87, 0x43, 0x7f, 0xc9, 0x5b, 0xfe, 0xe0, 0xba, - 0x30, 0xe1, 0xc4, 0xc7, 0xcc, 0x89, 0x0f, 0xd1, 0x83, 0x6c, 0x27, 0x7c, 0x06, 0xed, 0xbe, 0xbd, - 0x69, 0xb6, 0xe5, 0xd3, 0x84, 0x17, 0x7f, 0x96, 0xe0, 0x66, 0x8f, 0x10, 0x45, 0x1b, 0x39, 0x54, - 0xb2, 0x85, 0x70, 0xb9, 0x7e, 0x1d, 0x88, 0x60, 0xbe, 0xcd, 0x98, 0x6f, 0xa1, 0xcd, 0x6c, 0xe6, - 0x38, 0x82, 0x09, 0x45, 0xab, 0x5c, 0x46, 0x37, 0x72, 0x47, 0xb9, 0xe4, 0x5a, 0xba, 0x83, 0xfe, - 0x96, 0x28, 0x8e, 0xd4, 0x9d, 0x3c, 0x54, 0x71, 0x64, 0x49, 0x81, 0xa1, 0x8a, 0x23, 0xf3, 0xfa, - 0x97, 0xbf, 0xcf, 0x1c, 0xda, 0x44, 0x0f, 0x07, 0x14, 0x47, 0x8b, 0xa3, 0x35, 0xae, 0x52, 0x12, - 0xc5, 0x81, 0xfe, 0x25, 0xc1, 0x42, 0x5f, 0x99, 0x81, 0x3e, 0xca, 0x4b, 0x91, 0x01, 0x0a, 0xa7, - 0xbc, 0xf5, 0x6a, 0x60, 0xe1, 0xda, 0x2e, 0x73, 0xed, 0x11, 0xda, 0xea, 0x93, 0x65, 0x89, 0x05, - 0xae, 0xb8, 0x17, 0x1f, 0x1b, 0xfa, 0x9d, 0x04, 0xd0, 0x7d, 0xbb, 0x78, 0x8d, 0xb7, 0xc7, 0xd5, - 0x57, 0x16, 0x79, 0x95, 0x31, 0x5e, 0x46, 0x77, 0xfb, 0x30, 0x36, 0xcf, 0xa2, 0x26, 0x85, 0xbe, - 0x92, 0xe0, 0x56, 0xaf, 0x7a, 0x42, 0xf5, 0x61, 0x2e, 0xfb, 0xb4, 0x14, 0x2b, 0xbf, 0x7f, 0x2d, - 0x8c, 0x20, 0xfa, 0x1e, 0x23, 0xba, 0x86, 0x56, 0x06, 0xe8, 0x04, 0xfe, 0x59, 0x45, 0x33, 0x82, - 0xed, 0xdd, 0xaf, 0x5f, 0x54, 0xa4, 0xe7, 0x2f, 0x2a, 0xd2, 0xbf, 0x5f, 0x54, 0xa4, 0xdf, 0xbc, - 0xac, 0x8c, 0x3c, 0x7f, 0x59, 0x19, 0xf9, 0xc7, 0xcb, 0xca, 0xc8, 0x67, 0x6b, 0x89, 0xcf, 0x0c, - 0xa9, 0xd5, 0xda, 0xf7, 0x95, 0xf3, 0x78, 0x49, 0xf6, 0xb9, 0xe1, 0xa4, 0xc4, 0x3e, 0xf9, 0xbe, - 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbc, 0xf0, 0x40, 0x83, 0x97, 0x17, 0x00, 0x00, + // 2071 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0x5f, 0x6f, 0x1c, 0x57, + 0x15, 0xcf, 0x6c, 0xd6, 0x8e, 0x7d, 0x92, 0x38, 0xe1, 0xc6, 0x76, 0xec, 0xc5, 0xdd, 0x38, 0x93, + 0xa6, 0x75, 0xe2, 0x74, 0xa7, 0x76, 0x93, 0x36, 0x4a, 0xdd, 0x40, 0x6d, 0x27, 0xc1, 0x26, 0x10, + 0x7b, 0x4d, 0x40, 0xaa, 0x90, 0x46, 0xe3, 0xd9, 0xbb, 0xeb, 0xa9, 0x67, 0xe7, 0x4e, 0x67, 0xee, + 0x6c, 0x6c, 0xac, 0x05, 0x09, 0xbe, 0x00, 0x12, 0x20, 0xc1, 0x13, 0x2f, 0x95, 0xfa, 0xd4, 0x6f, + 0xc0, 0x1b, 0x12, 0xea, 0x03, 0x0f, 0x95, 0xfa, 0x82, 0x10, 0xaa, 0x50, 0xc2, 0x33, 0x88, 0x0f, + 0x80, 0x84, 0xe6, 0xde, 0x33, 0xff, 0xd6, 0xb3, 0xb3, 0xeb, 0xa4, 0x2f, 0xf1, 0xde, 0x99, 0x73, + 0xee, 0xf9, 0x9d, 0xff, 0xe7, 0x4c, 0x60, 0xde, 0x36, 0x3a, 0x86, 0x43, 0xb9, 0x16, 0xfe, 0xd5, + 0x5c, 0xc3, 0xf2, 0x2c, 0xa7, 0xa5, 0x7d, 0x12, 0x50, 0xef, 0xb0, 0xe6, 0x7a, 0x8c, 0x33, 0x32, + 0x89, 0x14, 0xb5, 0xf0, 0x6f, 0x0d, 0x29, 0x2a, 0x93, 0x2d, 0xd6, 0x62, 0x82, 0x40, 0x0b, 0x7f, + 0x49, 0xda, 0xca, 0x5c, 0x8b, 0xb1, 0x96, 0x4d, 0x35, 0xc3, 0xb5, 0x34, 0xc3, 0x71, 0x18, 0x37, + 0xb8, 0xc5, 0x1c, 0x1f, 0xdf, 0xde, 0x34, 0x99, 0xdf, 0x66, 0xbe, 0xb6, 0x6b, 0xf8, 0x54, 0x8a, + 0xd0, 0x3a, 0x4b, 0xbb, 0x94, 0x1b, 0x4b, 0x9a, 0x6b, 0xb4, 0x2c, 0x47, 0x10, 0x23, 0xed, 0xd5, + 0x5c, 0x5c, 0xae, 0xe1, 0x19, 0xed, 0xe8, 0xba, 0xeb, 0xb9, 0x24, 0x1e, 0x75, 0x03, 0x9e, 0xbe, + 0x69, 0x2e, 0x43, 0xe6, 0xbb, 0xd4, 0x14, 0xff, 0xe0, 0xdb, 0x2b, 0xd9, 0x4b, 0x6c, 0xc3, 0xf1, + 0x35, 0x97, 0xd9, 0x96, 0x89, 0xea, 0x57, 0x16, 0x33, 0x04, 0xd4, 0x65, 0xe6, 0x9e, 0xcf, 0x99, + 0x67, 0xb4, 0xa8, 0xe6, 0x73, 0x63, 0x9f, 0xea, 0xd4, 0xe1, 0x91, 0xad, 0x2a, 0xb7, 0xb2, 0xb2, + 0x82, 0x5d, 0xdf, 0xf4, 0x2c, 0x37, 0x04, 0x93, 0x39, 0x20, 0xf5, 0xb5, 0xac, 0x6c, 0x8f, 0x7d, + 0x4c, 0x4d, 0xee, 0x47, 0x3f, 0x90, 0xe8, 0xcd, 0x0c, 0x51, 0x83, 0x3d, 0x73, 0xb8, 0xd5, 0xa6, + 0x5a, 0x67, 0x29, 0xfe, 0x2d, 0x09, 0xd5, 0x49, 0x20, 0xdb, 0xa1, 0x4d, 0xb7, 0x84, 0x8d, 0xea, + 0xf4, 0x93, 0x80, 0xfa, 0x5c, 0xdd, 0x86, 0x4b, 0x99, 0xa7, 0xbe, 0xcb, 0x1c, 0x9f, 0x92, 0x7b, + 0x30, 0x2a, 0x6d, 0x39, 0xa3, 0xcc, 0x2b, 0x0b, 0x67, 0x97, 0xe7, 0x6a, 0x79, 0x5e, 0xae, 0x49, + 0xae, 0xd5, 0xf2, 0x17, 0x5f, 0x5f, 0x39, 0x55, 0x47, 0x0e, 0x75, 0x1b, 0xa6, 0xe4, 0x95, 0x1e, + 0xeb, 0x58, 0x0d, 0xea, 0x45, 0xb2, 0xc8, 0x0c, 0x9c, 0x31, 0xf7, 0x0c, 0xcb, 0xd9, 0x58, 0x17, + 0xb7, 0x8e, 0xd7, 0xa3, 0x23, 0xa9, 0x02, 0xf8, 0x7b, 0xec, 0xd9, 0x43, 0x8f, 0xfd, 0x8c, 0x3a, + 0x33, 0xa5, 0x79, 0x65, 0x61, 0xac, 0x9e, 0x7a, 0xa2, 0xee, 0xc3, 0x74, 0xef, 0x95, 0x08, 0xf4, + 0xfb, 0x00, 0xc2, 0xcc, 0x0f, 0x42, 0x2b, 0xcf, 0x28, 0xf3, 0xa7, 0x17, 0xce, 0x2e, 0x5f, 0xcf, + 0x82, 0x4d, 0xfb, 0xa4, 0xb6, 0x13, 0x13, 0x23, 0xea, 0x14, 0xfb, 0x66, 0x79, 0xac, 0x74, 0xf1, + 0xb4, 0xba, 0x09, 0x93, 0x19, 0x61, 0x29, 0xf8, 0x46, 0xa3, 0xe1, 0x51, 0xdf, 0x8f, 0xe0, 0xe3, + 0x31, 0xad, 0x58, 0x29, 0xa3, 0x98, 0xba, 0xd7, 0x63, 0x8b, 0x18, 0xf7, 0x13, 0x38, 0x17, 0x0b, + 0xb6, 0xa8, 0xff, 0x32, 0xc8, 0x33, 0x17, 0xa8, 0x9b, 0x68, 0xa2, 0x47, 0x94, 0x6f, 0x49, 0xef, + 0x0c, 0x36, 0xfb, 0x34, 0x8c, 0x9a, 0xb6, 0x45, 0x1d, 0x8e, 0xb0, 0xf1, 0xa4, 0x7e, 0x5e, 0x82, + 0xcb, 0xc7, 0x2e, 0x43, 0xe0, 0x1b, 0x30, 0xee, 0x46, 0x5e, 0x78, 0x19, 0xd4, 0x09, 0x37, 0xb9, + 0x06, 0xe7, 0xcd, 0xc0, 0xf3, 0xa8, 0xc3, 0x75, 0xc1, 0x23, 0x50, 0x94, 0xeb, 0xe7, 0xf0, 0xe1, + 0x83, 0xf0, 0x19, 0xb9, 0x0b, 0xb3, 0x61, 0x10, 0xeb, 0x36, 0x6d, 0x72, 0x9d, 0x33, 0xdd, 0xa1, + 0x07, 0x5c, 0xc7, 0xf8, 0x9b, 0x39, 0x2d, 0x18, 0xa6, 0x42, 0x82, 0xc7, 0xb4, 0xc9, 0x7f, 0xc4, + 0x7e, 0x48, 0x0f, 0x22, 0xc4, 0xe4, 0x0e, 0x5c, 0x0e, 0x13, 0x59, 0xb7, 0x0d, 0x9f, 0xeb, 0x81, + 0xdb, 0x30, 0x38, 0x6d, 0xe8, 0xbb, 0x36, 0x33, 0xf7, 0x67, 0xca, 0x82, 0x6f, 0x32, 0x7c, 0xfd, + 0xd8, 0xf0, 0xf9, 0x53, 0xf9, 0x72, 0x35, 0x7c, 0x47, 0x96, 0x60, 0x4a, 0x10, 0xe9, 0xac, 0x99, + 0x15, 0x36, 0x22, 0x98, 0x88, 0x78, 0xf9, 0xa4, 0x99, 0x92, 0xa4, 0xfe, 0x02, 0x66, 0x85, 0xb9, + 0x7e, 0x4c, 0x3d, 0xab, 0x79, 0xf8, 0xaa, 0xe6, 0x27, 0x15, 0x18, 0x8b, 0x8c, 0x24, 0x34, 0x1c, + 0xaf, 0xc7, 0x67, 0x32, 0x09, 0x23, 0x69, 0x15, 0xe4, 0x41, 0xfd, 0x54, 0x81, 0x4a, 0x1e, 0x02, + 0xf4, 0xd9, 0x24, 0x8c, 0x74, 0x0c, 0xdb, 0x6a, 0x08, 0x00, 0x63, 0x75, 0x79, 0x20, 0x37, 0xe0, + 0x62, 0xa8, 0x1a, 0x6d, 0xe8, 0x89, 0x43, 0xa5, 0x41, 0x2f, 0xc8, 0xe7, 0x71, 0xb6, 0x91, 0x79, + 0x38, 0x67, 0x06, 0xba, 0x4b, 0x3d, 0x74, 0x94, 0x14, 0x0e, 0x66, 0xb0, 0x45, 0x3d, 0xe9, 0xa6, + 0xd7, 0x00, 0xb0, 0x2e, 0xe9, 0x56, 0x43, 0x98, 0x6a, 0x5c, 0xb8, 0x3a, 0x7c, 0xb2, 0xd1, 0xc0, + 0xcc, 0xfa, 0x9d, 0x02, 0x57, 0x33, 0xe9, 0x80, 0x40, 0xd7, 0xf6, 0x0c, 0xc7, 0xa4, 0x91, 0xc1, + 0xd2, 0xea, 0x2b, 0x3d, 0xea, 0xf7, 0xcd, 0x34, 0x32, 0x0f, 0x67, 0x5b, 0x94, 0xd9, 0xcc, 0x14, + 0xb5, 0x5d, 0x28, 0x32, 0x52, 0x4f, 0x3f, 0x12, 0xbc, 0x76, 0xe0, 0x73, 0xea, 0x09, 0xfc, 0x21, + 0xaf, 0x3c, 0xaa, 0x36, 0xa8, 0x45, 0xb0, 0xd0, 0x8a, 0x0f, 0x61, 0xd4, 0x14, 0x4f, 0x24, 0xaa, + 0xd5, 0x5a, 0x18, 0xcf, 0x7f, 0xff, 0xfa, 0xca, 0x1b, 0x2d, 0x8b, 0xef, 0x05, 0xbb, 0x35, 0x93, + 0xb5, 0x35, 0xec, 0x60, 0xf2, 0xcf, 0x5b, 0x7e, 0x63, 0x5f, 0xe3, 0x87, 0x2e, 0xf5, 0x6b, 0xeb, + 0xd4, 0xac, 0x23, 0xb7, 0x6a, 0x60, 0x4d, 0x78, 0xea, 0x53, 0x4f, 0x64, 0xc6, 0x2b, 0x14, 0x98, + 0x24, 0x1e, 0x4e, 0xa7, 0xe3, 0xe1, 0x19, 0x16, 0x83, 0x94, 0x08, 0x54, 0xe2, 0x11, 0x8c, 0x99, + 0xcc, 0xf1, 0x83, 0x36, 0x1a, 0xf7, 0x84, 0xd9, 0x1b, 0x33, 0x87, 0x82, 0xdb, 0xc6, 0xc1, 0xda, + 0x53, 0x4c, 0x5a, 0x79, 0x50, 0xdf, 0x87, 0x2b, 0x42, 0xf0, 0x4e, 0xd8, 0x62, 0xcd, 0x38, 0x80, + 0x1e, 0x5b, 0x3e, 0x1f, 0x98, 0x0f, 0x6a, 0x1b, 0xe6, 0xfb, 0x33, 0x7f, 0xe3, 0xe5, 0x47, 0xdd, + 0x86, 0x6f, 0x0b, 0x71, 0x0f, 0x9a, 0x4d, 0x6a, 0x72, 0xab, 0x43, 0xb7, 0x44, 0x5f, 0x4f, 0x85, + 0x61, 0xc6, 0x52, 0xe3, 0x29, 0xe5, 0xa7, 0x61, 0x34, 0xac, 0x1d, 0xb1, 0x3b, 0xf0, 0x14, 0x06, + 0xf8, 0x5c, 0xfe, 0x9d, 0x08, 0x7f, 0x19, 0x46, 0xe5, 0xf4, 0x80, 0xc6, 0xaf, 0xf4, 0xf4, 0xd5, + 0x70, 0xbe, 0xa8, 0x21, 0x0f, 0x52, 0x92, 0x0f, 0x61, 0xc2, 0xa5, 0x4e, 0xc3, 0x72, 0x5a, 0x3a, + 0xf2, 0x96, 0x06, 0xf2, 0x9e, 0x47, 0x0e, 0x79, 0x54, 0xff, 0xab, 0x60, 0x41, 0xdf, 0x69, 0xec, + 0xf7, 0x16, 0x87, 0x47, 0x70, 0x26, 0xaa, 0x70, 0x12, 0xd3, 0x5b, 0xf9, 0xbd, 0xbe, 0x4f, 0x43, + 0xa8, 0x47, 0xdc, 0x64, 0x0a, 0x46, 0xdb, 0xc6, 0x81, 0x6e, 0x06, 0xe9, 0x90, 0x08, 0xc8, 0x22, + 0x94, 0x43, 0xeb, 0x88, 0x00, 0x3d, 0xbb, 0x7c, 0x39, 0x7b, 0xb9, 0x98, 0xb4, 0x76, 0x5c, 0x6a, + 0xd6, 0x05, 0x11, 0xd9, 0x80, 0x0b, 0xd1, 0xd8, 0xa2, 0xe3, 0x00, 0x52, 0x16, 0x7c, 0xf3, 0x59, + 0xbe, 0x78, 0xb6, 0xe9, 0x2c, 0xe1, 0x10, 0x52, 0x9f, 0x88, 0x9e, 0xc9, 0xb3, 0xfa, 0x9d, 0x9e, + 0x5a, 0xf3, 0x03, 0xe6, 0xf0, 0x3d, 0xfb, 0x70, 0xcb, 0x38, 0x64, 0x01, 0x1f, 0xa2, 0xd6, 0xa8, + 0xfb, 0x40, 0x76, 0x52, 0x43, 0x99, 0x64, 0x24, 0x2a, 0x9c, 0x4b, 0x8f, 0x6a, 0xc8, 0x95, 0x79, + 0x46, 0x66, 0x61, 0x4c, 0xc4, 0x74, 0x58, 0x0a, 0x33, 0xf9, 0xda, 0x08, 0x23, 0xc7, 0x68, 0xb3, + 0xc0, 0xe1, 0x98, 0xb0, 0x78, 0x52, 0x7f, 0xde, 0x53, 0x82, 0x7a, 0xd0, 0x26, 0x85, 0x9c, 0x33, + 0x6e, 0xd8, 0x42, 0x6a, 0xb9, 0x2e, 0x0f, 0x64, 0x15, 0xce, 0x34, 0x28, 0x37, 0x2c, 0xdb, 0x9f, + 0x29, 0x89, 0x8c, 0x58, 0xc8, 0xf7, 0xe0, 0x71, 0x6d, 0xea, 0x11, 0xa3, 0xba, 0x0e, 0x13, 0x49, + 0xf5, 0x13, 0x8a, 0x16, 0x95, 0xe1, 0x44, 0x8b, 0x52, 0x46, 0x8b, 0x8f, 0xe1, 0xfc, 0x9a, 0x4c, + 0x66, 0xbc, 0x24, 0x6d, 0x09, 0x25, 0x6b, 0x89, 0xfb, 0x61, 0xdc, 0x85, 0x44, 0x11, 0xea, 0xd7, + 0xfb, 0xcc, 0x98, 0x19, 0x58, 0xf5, 0x88, 0x49, 0x5d, 0x83, 0xeb, 0x32, 0xa4, 0x53, 0x5a, 0xf5, + 0xf3, 0x71, 0xbf, 0x44, 0x56, 0xbb, 0xf0, 0xc6, 0xa0, 0x4b, 0x0a, 0x4d, 0xff, 0x41, 0xaf, 0xe9, + 0xaf, 0xe5, 0x2b, 0x91, 0xb1, 0x4a, 0x62, 0xf5, 0x2a, 0x96, 0x8b, 0xb8, 0xd6, 0x89, 0x66, 0xba, + 0x16, 0x44, 0xd3, 0xb9, 0x0e, 0xaf, 0xf5, 0x79, 0x8f, 0xa8, 0xee, 0x43, 0xd9, 0x72, 0x9a, 0x0c, + 0x2b, 0xe1, 0x00, 0x0b, 0xae, 0x05, 0x1b, 0x4e, 0x93, 0x61, 0x21, 0x14, 0x7c, 0xea, 0x4a, 0xe2, + 0x76, 0xf9, 0xb6, 0xd0, 0xed, 0x13, 0x50, 0x8a, 0xb3, 0xbb, 0x64, 0x06, 0xaa, 0x0b, 0xd5, 0x9e, + 0xe9, 0x36, 0xda, 0xad, 0x5e, 0xad, 0x97, 0xa7, 0x3a, 0xf5, 0xe9, 0x6c, 0xa7, 0xfe, 0x4c, 0x81, + 0x89, 0x44, 0xca, 0xba, 0xc1, 0x0d, 0x42, 0xa0, 0xec, 0x19, 0xce, 0x3e, 0xfa, 0x45, 0xfc, 0x26, + 0x73, 0xe9, 0x2e, 0x21, 0xf1, 0xa6, 0xe6, 0x4e, 0x0d, 0x2e, 0xb1, 0x0e, 0xf5, 0x0c, 0xdb, 0x0e, + 0x47, 0x9a, 0x26, 0xf3, 0xda, 0xa2, 0xab, 0x4b, 0x51, 0x04, 0x5f, 0x6d, 0x25, 0x6f, 0xd2, 0x48, + 0xcb, 0x7d, 0x91, 0x8e, 0x64, 0x91, 0x1a, 0xd8, 0x09, 0xf3, 0x6c, 0x93, 0x38, 0xaf, 0x61, 0x70, + 0xa3, 0xd8, 0x79, 0x59, 0x6d, 0x23, 0xe7, 0x85, 0x7c, 0x6a, 0x80, 0x19, 0x70, 0x5c, 0xc4, 0xba, + 0x8c, 0xaf, 0x57, 0x19, 0x2c, 0xfa, 0xfb, 0xe0, 0x7f, 0x0a, 0x7c, 0x2b, 0x2d, 0xaa, 0x23, 0xdc, + 0xf0, 0x10, 0x20, 0x59, 0xad, 0xb1, 0x93, 0xcc, 0x0f, 0x52, 0x29, 0xda, 0xc1, 0x12, 0x4e, 0x62, + 0xc3, 0x4c, 0x72, 0x8a, 0x66, 0x6f, 0xdd, 0x37, 0x99, 0x47, 0xb1, 0xef, 0xdd, 0x1a, 0x74, 0x2b, + 0x76, 0xa8, 0x9d, 0x90, 0x07, 0x25, 0x4c, 0x7b, 0xb9, 0x6f, 0x5f, 0xca, 0xb3, 0xfb, 0x58, 0x33, + 0x0a, 0xcc, 0x8e, 0x0e, 0xfe, 0x30, 0xe3, 0xe0, 0x37, 0x07, 0x3a, 0x58, 0x9a, 0x32, 0xed, 0xe3, + 0xe5, 0xdf, 0x4f, 0xc3, 0x88, 0x90, 0x46, 0x7e, 0xa5, 0xc0, 0xa8, 0x6c, 0x6d, 0x64, 0xa1, 0xa0, + 0x43, 0x67, 0xd6, 0xfb, 0xca, 0x8d, 0x21, 0x28, 0x25, 0x58, 0xf5, 0xf5, 0x5f, 0x7e, 0xf5, 0xaf, + 0xdf, 0x94, 0xaa, 0x64, 0x4e, 0x2b, 0xf8, 0xb4, 0x42, 0xfe, 0xa0, 0xc0, 0x78, 0xb2, 0x17, 0x2c, + 0x16, 0x5d, 0xdf, 0xb3, 0xfe, 0x57, 0x6e, 0x0d, 0x47, 0x8c, 0x70, 0x96, 0x04, 0x9c, 0x45, 0x72, + 0xa3, 0x0f, 0x9c, 0x88, 0x41, 0x3b, 0x42, 0x8f, 0x75, 0xc9, 0x1f, 0x15, 0x18, 0x8b, 0x2e, 0x22, + 0x37, 0x87, 0x90, 0x16, 0x21, 0x5b, 0x1c, 0x8a, 0x16, 0x81, 0xdd, 0x13, 0xc0, 0x6e, 0x93, 0xe5, + 0x62, 0x60, 0xda, 0x11, 0xe6, 0x58, 0x37, 0x85, 0xf0, 0x33, 0x05, 0x20, 0x19, 0xa1, 0xc8, 0xad, + 0x21, 0x27, 0x2d, 0x89, 0xf2, 0x64, 0x73, 0x99, 0xba, 0x22, 0x70, 0xbe, 0x4b, 0x6e, 0xe7, 0xe3, + 0x6c, 0xd1, 0x78, 0xb3, 0x4d, 0x00, 0x6a, 0x47, 0x72, 0x05, 0xed, 0x92, 0xbf, 0x28, 0x70, 0x3e, + 0xb3, 0x4c, 0x12, 0xad, 0x40, 0x7c, 0xde, 0xe2, 0x5b, 0x79, 0x7b, 0x78, 0x06, 0x84, 0x5c, 0x17, + 0x90, 0x1f, 0x93, 0xcd, 0x7c, 0xc8, 0x1d, 0xc1, 0x54, 0x80, 0x5a, 0x3b, 0x8a, 0xac, 0xdf, 0xd5, + 0x8e, 0xc4, 0x26, 0xd4, 0x25, 0xff, 0x56, 0x60, 0x2a, 0x77, 0xaf, 0x23, 0xef, 0x0d, 0xe1, 0xf5, + 0xbc, 0x05, 0xb5, 0x72, 0xf7, 0xe4, 0x8c, 0xa8, 0xe0, 0xae, 0x50, 0xf0, 0xa7, 0xe4, 0xa3, 0xe2, + 0xd8, 0x89, 0xcb, 0x9e, 0xdc, 0x18, 0x33, 0x6a, 0x25, 0x4a, 0xa7, 0x76, 0x5b, 0x61, 0x02, 0x51, + 0x9d, 0xba, 0xe4, 0x53, 0x05, 0xc6, 0xe3, 0xbd, 0xaf, 0x30, 0x43, 0x7b, 0x17, 0xd0, 0xc2, 0x0c, + 0x3d, 0xb6, 0x4a, 0x0e, 0x0a, 0xb0, 0xc0, 0xa7, 0x9e, 0xfc, 0xf8, 0x99, 0x9b, 0x0a, 0x7f, 0x56, + 0xe0, 0x52, 0xce, 0xa2, 0x47, 0xee, 0x14, 0x60, 0xe8, 0xbf, 0x55, 0x56, 0xde, 0x3d, 0x29, 0x1b, + 0x2a, 0xf1, 0x81, 0x50, 0xe2, 0x3d, 0x72, 0x27, 0x5f, 0x09, 0x5f, 0xb0, 0x26, 0x1f, 0x48, 0x74, + 0xdb, 0xf2, 0x79, 0x4a, 0x8b, 0x3f, 0x29, 0x70, 0xa1, 0x67, 0xd7, 0x23, 0x4b, 0x05, 0x50, 0xf2, + 0x77, 0xcd, 0xca, 0xf2, 0x49, 0x58, 0x10, 0xf9, 0xaa, 0x40, 0xbe, 0x42, 0xee, 0xe5, 0x23, 0xa7, + 0x11, 0x1b, 0x2e, 0x8d, 0xda, 0x51, 0x34, 0xf4, 0x76, 0xb5, 0x23, 0xb9, 0xae, 0x76, 0xc9, 0x5f, + 0x53, 0xc9, 0x91, 0x19, 0x7b, 0x87, 0x4a, 0x8e, 0xbc, 0x69, 0x7b, 0xa8, 0xe4, 0xc8, 0x9d, 0xb0, + 0xd5, 0xef, 0x0a, 0x85, 0xee, 0x91, 0xbb, 0x03, 0x92, 0xa3, 0x2d, 0xb9, 0x75, 0xb9, 0x08, 0xa4, + 0x92, 0x83, 0xfc, 0x43, 0x81, 0xd9, 0xbe, 0x93, 0x3c, 0x79, 0xbf, 0x28, 0x44, 0x06, 0x2c, 0x11, + 0x95, 0x95, 0x97, 0x63, 0x46, 0xd5, 0xd6, 0x85, 0x6a, 0xf7, 0xc9, 0x4a, 0x9f, 0x28, 0x4b, 0x5d, + 0x70, 0x4c, 0xbd, 0xd8, 0x6d, 0xe4, 0xb7, 0x0a, 0x40, 0xb2, 0xc0, 0x7f, 0x83, 0xdd, 0xe3, 0xf8, + 0x57, 0x01, 0xf5, 0x86, 0x40, 0x7c, 0x8d, 0x5c, 0xed, 0x83, 0xb8, 0xb1, 0x1f, 0x15, 0x29, 0xf2, + 0xb9, 0x02, 0x17, 0x7b, 0x17, 0x14, 0xb2, 0x3c, 0x4c, 0xb3, 0xcf, 0x6e, 0x3b, 0x95, 0x77, 0x4e, + 0xc4, 0x83, 0x40, 0xdf, 0x16, 0x40, 0x6f, 0x92, 0x85, 0x01, 0x73, 0x82, 0xfc, 0x72, 0xa9, 0x9b, + 0x01, 0xf9, 0x4a, 0x01, 0x72, 0x7c, 0x76, 0x23, 0xb7, 0x87, 0x1a, 0x02, 0x7a, 0x16, 0x9c, 0xca, + 0x9d, 0x13, 0x72, 0x21, 0xea, 0x2d, 0x81, 0x7a, 0x93, 0x7c, 0x6f, 0x40, 0xac, 0x27, 0x63, 0x6b, + 0x9f, 0x26, 0x10, 0x97, 0xfd, 0xff, 0x28, 0x30, 0xdb, 0x77, 0x22, 0x2d, 0x8c, 0xfd, 0x41, 0xeb, + 0x43, 0x61, 0xec, 0x0f, 0x1c, 0x82, 0xd5, 0x9f, 0x08, 0x55, 0xb7, 0xc9, 0x93, 0xa1, 0x55, 0xd5, + 0x71, 0x3d, 0xce, 0xeb, 0x1b, 0x89, 0xc6, 0xab, 0xeb, 0x5f, 0x3c, 0xaf, 0x2a, 0x5f, 0x3e, 0xaf, + 0x2a, 0xff, 0x7c, 0x5e, 0x55, 0x7e, 0xfd, 0xa2, 0x7a, 0xea, 0xcb, 0x17, 0xd5, 0x53, 0x7f, 0x7b, + 0x51, 0x3d, 0xf5, 0xd1, 0xcd, 0xd4, 0x17, 0xd9, 0x8c, 0xd0, 0xce, 0x6d, 0xed, 0x20, 0x96, 0x2c, + 0xbe, 0xcc, 0xee, 0x8e, 0x8a, 0xff, 0x1d, 0x7b, 0xe7, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x13, + 0x5b, 0x16, 0xf6, 0xe9, 0x1c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1732,6 +2109,10 @@ type QueryClient interface { SdkPairing(ctx context.Context, in *QueryGetPairingRequest, opts ...grpc.CallOption) (*QuerySdkPairingResponse, error) // Queries a for the aggregated CU of all ProviderEpochCu objects all the providers. ProvidersEpochCu(ctx context.Context, in *QueryProvidersEpochCuRequest, opts ...grpc.CallOption) (*QueryProvidersEpochCuResponse, error) + // Queries a for a provider reputation. + ProviderReputation(ctx context.Context, in *QueryProviderReputationRequest, opts ...grpc.CallOption) (*QueryProviderReputationResponse, error) + // Queries a for a provider reputation's details (mainly for developers). + ProviderReputationDetails(ctx context.Context, in *QueryProviderReputationDetailsRequest, opts ...grpc.CallOption) (*QueryProviderReputationDetailsResponse, error) } type queryClient struct { @@ -1859,6 +2240,24 @@ func (c *queryClient) ProvidersEpochCu(ctx context.Context, in *QueryProvidersEp return out, nil } +func (c *queryClient) ProviderReputation(ctx context.Context, in *QueryProviderReputationRequest, opts ...grpc.CallOption) (*QueryProviderReputationResponse, error) { + out := new(QueryProviderReputationResponse) + err := c.cc.Invoke(ctx, "/lavanet.lava.pairing.Query/ProviderReputation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ProviderReputationDetails(ctx context.Context, in *QueryProviderReputationDetailsRequest, opts ...grpc.CallOption) (*QueryProviderReputationDetailsResponse, error) { + out := new(QueryProviderReputationDetailsResponse) + err := c.cc.Invoke(ctx, "/lavanet.lava.pairing.Query/ProviderReputationDetails", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Parameters queries the parameters of the module. @@ -1887,6 +2286,10 @@ type QueryServer interface { SdkPairing(context.Context, *QueryGetPairingRequest) (*QuerySdkPairingResponse, error) // Queries a for the aggregated CU of all ProviderEpochCu objects all the providers. ProvidersEpochCu(context.Context, *QueryProvidersEpochCuRequest) (*QueryProvidersEpochCuResponse, error) + // Queries a for a provider reputation. + ProviderReputation(context.Context, *QueryProviderReputationRequest) (*QueryProviderReputationResponse, error) + // Queries a for a provider reputation's details (mainly for developers). + ProviderReputationDetails(context.Context, *QueryProviderReputationDetailsRequest) (*QueryProviderReputationDetailsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -1932,6 +2335,12 @@ func (*UnimplementedQueryServer) SdkPairing(ctx context.Context, req *QueryGetPa func (*UnimplementedQueryServer) ProvidersEpochCu(ctx context.Context, req *QueryProvidersEpochCuRequest) (*QueryProvidersEpochCuResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ProvidersEpochCu not implemented") } +func (*UnimplementedQueryServer) ProviderReputation(ctx context.Context, req *QueryProviderReputationRequest) (*QueryProviderReputationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ProviderReputation not implemented") +} +func (*UnimplementedQueryServer) ProviderReputationDetails(ctx context.Context, req *QueryProviderReputationDetailsRequest) (*QueryProviderReputationDetailsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ProviderReputationDetails not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -2171,24 +2580,60 @@ func _Query_ProvidersEpochCu_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } -var _Query_serviceDesc = grpc.ServiceDesc{ - ServiceName: "lavanet.lava.pairing.Query", - HandlerType: (*QueryServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Params", - Handler: _Query_Params_Handler, - }, - { - MethodName: "Providers", - Handler: _Query_Providers_Handler, - }, - { - MethodName: "Provider", - Handler: _Query_Provider_Handler, - }, - { - MethodName: "GetPairing", +func _Query_ProviderReputation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryProviderReputationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ProviderReputation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lavanet.lava.pairing.Query/ProviderReputation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ProviderReputation(ctx, req.(*QueryProviderReputationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ProviderReputationDetails_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryProviderReputationDetailsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ProviderReputationDetails(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lavanet.lava.pairing.Query/ProviderReputationDetails", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ProviderReputationDetails(ctx, req.(*QueryProviderReputationDetailsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "lavanet.lava.pairing.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "Providers", + Handler: _Query_Providers_Handler, + }, + { + MethodName: "Provider", + Handler: _Query_Provider_Handler, + }, + { + MethodName: "GetPairing", Handler: _Query_GetPairing_Handler, }, { @@ -2227,6 +2672,14 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "ProvidersEpochCu", Handler: _Query_ProvidersEpochCu_Handler, }, + { + MethodName: "ProviderReputation", + Handler: _Query_ProviderReputation_Handler, + }, + { + MethodName: "ProviderReputationDetails", + Handler: _Query_ProviderReputationDetails_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "lavanet/lava/pairing/query.proto", @@ -3369,122 +3822,395 @@ func (m *ProviderCuInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { - offset -= sovQuery(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *QueryParamsRequest) Size() (n int) { - if m == nil { - return 0 +func (m *QueryProviderReputationRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - var l int - _ = l - return n + return dAtA[:n], nil } -func (m *QueryParamsResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = m.Params.Size() - n += 1 + l + sovQuery(uint64(l)) - return n +func (m *QueryProviderReputationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryProvidersRequest) Size() (n int) { - if m == nil { - return 0 - } +func (m *QueryProviderReputationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.ChainID) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) + if len(m.Cluster) > 0 { + i -= len(m.Cluster) + copy(dAtA[i:], m.Cluster) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Cluster))) + i-- + dAtA[i] = 0x1a } - if m.ShowFrozen { - n += 2 + if len(m.ChainID) > 0 { + i -= len(m.ChainID) + copy(dAtA[i:], m.ChainID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainID))) + i-- + dAtA[i] = 0x12 } - return n + if len(m.Provider) > 0 { + i -= len(m.Provider) + copy(dAtA[i:], m.Provider) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Provider))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func (m *QueryProvidersResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.StakeEntry) > 0 { - for _, e := range m.StakeEntry { - l = e.Size() - n += 1 + l + sovQuery(uint64(l)) - } +func (m *ReputationData) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *QueryProviderRequest) Size() (n int) { - if m == nil { - return 0 - } +func (m *ReputationData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReputationData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Address) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) + if len(m.Cluster) > 0 { + i -= len(m.Cluster) + copy(dAtA[i:], m.Cluster) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Cluster))) + i-- + dAtA[i] = 0x2a } - l = len(m.ChainID) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) + if len(m.ChainID) > 0 { + i -= len(m.ChainID) + copy(dAtA[i:], m.ChainID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainID))) + i-- + dAtA[i] = 0x22 } - return n + if len(m.OverallPerformance) > 0 { + i -= len(m.OverallPerformance) + copy(dAtA[i:], m.OverallPerformance) + i = encodeVarintQuery(dAtA, i, uint64(len(m.OverallPerformance))) + i-- + dAtA[i] = 0x1a + } + if m.Providers != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Providers)) + i-- + dAtA[i] = 0x10 + } + if m.Rank != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Rank)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } -func (m *QueryProviderResponse) Size() (n int) { - if m == nil { - return 0 +func (m *QueryProviderReputationResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } + return dAtA[:n], nil +} + +func (m *QueryProviderReputationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProviderReputationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.StakeEntries) > 0 { - for _, e := range m.StakeEntries { - l = e.Size() - n += 1 + l + sovQuery(uint64(l)) + if len(m.Data) > 0 { + for iNdEx := len(m.Data) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Data[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } } - return n + return len(dAtA) - i, nil } -func (m *QueryGetPairingRequest) Size() (n int) { - if m == nil { - return 0 +func (m *QueryProviderReputationDetailsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } + return dAtA[:n], nil +} + +func (m *QueryProviderReputationDetailsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProviderReputationDetailsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.ChainID) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) + if len(m.Cluster) > 0 { + i -= len(m.Cluster) + copy(dAtA[i:], m.Cluster) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Cluster))) + i-- + dAtA[i] = 0x1a } - l = len(m.Client) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) + if len(m.ChainID) > 0 { + i -= len(m.ChainID) + copy(dAtA[i:], m.ChainID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainID))) + i-- + dAtA[i] = 0x12 } - return n + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func (m *QueryGetPairingResponse) Size() (n int) { - if m == nil { - return 0 +func (m *ReputationDevData) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - var l int + return dAtA[:n], nil +} + +func (m *ReputationDevData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReputationDevData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Cluster) > 0 { + i -= len(m.Cluster) + copy(dAtA[i:], m.Cluster) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Cluster))) + i-- + dAtA[i] = 0x2a + } + if len(m.ChainID) > 0 { + i -= len(m.ChainID) + copy(dAtA[i:], m.ChainID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainID))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.ReputationPairingScore.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Reputation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryProviderReputationDetailsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryProviderReputationDetailsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProviderReputationDetailsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + for iNdEx := len(m.Data) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Data[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryProvidersRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.ShowFrozen { + n += 2 + } + return n +} + +func (m *QueryProvidersResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.StakeEntry) > 0 { + for _, e := range m.StakeEntry { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryProviderRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryProviderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.StakeEntries) > 0 { + for _, e := range m.StakeEntries { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryGetPairingRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Client) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryGetPairingResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int _ = l if len(m.Providers) > 0 { for _, e := range m.Providers { @@ -3865,65 +4591,185 @@ func (m *ProviderCuInfo) Size() (n int) { return n } -func sovQuery(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozQuery(x uint64) (n int) { - return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } +func (m *QueryProviderReputationRequest) Size() (n int) { + if m == nil { + return 0 } - - if iNdEx > l { - return io.ErrUnexpectedEOF + var l int + _ = l + l = len(m.Provider) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) } - return nil -} -func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Cluster) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *ReputationData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Rank != 0 { + n += 1 + sovQuery(uint64(m.Rank)) + } + if m.Providers != 0 { + n += 1 + sovQuery(uint64(m.Providers)) + } + l = len(m.OverallPerformance) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Cluster) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryProviderReputationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Data) > 0 { + for _, e := range m.Data { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryProviderReputationDetailsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Cluster) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *ReputationDevData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Reputation.Size() + n += 1 + l + sovQuery(uint64(l)) + l = m.ReputationPairingScore.Size() + n += 1 + l + sovQuery(uint64(l)) + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Cluster) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryProviderReputationDetailsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Data) > 0 { + for _, e := range m.Data { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 @@ -6985,6 +7831,830 @@ func (m *ProviderCuInfo) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryProviderReputationRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProviderReputationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProviderReputationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Provider", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Provider = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cluster = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReputationData) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReputationData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReputationData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Rank", wireType) + } + m.Rank = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Rank |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Providers", wireType) + } + m.Providers = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Providers |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OverallPerformance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OverallPerformance = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cluster = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryProviderReputationResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProviderReputationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProviderReputationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data, ReputationData{}) + if err := m.Data[len(m.Data)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryProviderReputationDetailsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProviderReputationDetailsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProviderReputationDetailsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cluster = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReputationDevData) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReputationDevData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReputationDevData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reputation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Reputation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReputationPairingScore", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ReputationPairingScore.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cluster = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryProviderReputationDetailsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProviderReputationDetailsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProviderReputationDetailsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data, ReputationDevData{}) + if err := m.Data[len(m.Data)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/pairing/types/query.pb.gw.go b/x/pairing/types/query.pb.gw.go index 8eab096ed7..555593a107 100644 --- a/x/pairing/types/query.pb.gw.go +++ b/x/pairing/types/query.pb.gw.go @@ -901,6 +901,202 @@ func local_request_Query_ProvidersEpochCu_0(ctx context.Context, marshaler runti } +func request_Query_ProviderReputation_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProviderReputationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["provider"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider") + } + + protoReq.Provider, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider", err) + } + + val, ok = pathParams["chainID"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chainID") + } + + protoReq.ChainID, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chainID", err) + } + + val, ok = pathParams["cluster"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "cluster") + } + + protoReq.Cluster, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "cluster", err) + } + + msg, err := client.ProviderReputation(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ProviderReputation_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProviderReputationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["provider"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider") + } + + protoReq.Provider, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider", err) + } + + val, ok = pathParams["chainID"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chainID") + } + + protoReq.ChainID, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chainID", err) + } + + val, ok = pathParams["cluster"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "cluster") + } + + protoReq.Cluster, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "cluster", err) + } + + msg, err := server.ProviderReputation(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ProviderReputationDetails_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProviderReputationDetailsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + val, ok = pathParams["chainID"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chainID") + } + + protoReq.ChainID, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chainID", err) + } + + val, ok = pathParams["cluster"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "cluster") + } + + protoReq.Cluster, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "cluster", err) + } + + msg, err := client.ProviderReputationDetails(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ProviderReputationDetails_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProviderReputationDetailsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + val, ok = pathParams["chainID"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chainID") + } + + protoReq.ChainID, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chainID", err) + } + + val, ok = pathParams["cluster"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "cluster") + } + + protoReq.Cluster, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "cluster", err) + } + + msg, err := server.ProviderReputationDetails(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -1206,6 +1402,52 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_ProviderReputation_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ProviderReputation_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ProviderReputation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ProviderReputationDetails_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ProviderReputationDetails_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ProviderReputationDetails_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1507,6 +1749,46 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_ProviderReputation_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ProviderReputation_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ProviderReputation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ProviderReputationDetails_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ProviderReputationDetails_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ProviderReputationDetails_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1536,6 +1818,10 @@ var ( pattern_Query_SdkPairing_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"lavanet", "lava", "pairing", "sdk_pairing"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_ProvidersEpochCu_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"lavanet", "lava", "pairing", "providers_epoch_cu"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ProviderReputation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6}, []string{"lavanet", "lava", "pairing", "provider_reputation", "provider", "chainID", "cluster"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ProviderReputationDetails_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6}, []string{"lavanet", "lava", "pairing", "provider_reputation_details", "address", "chainID", "cluster"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -1564,4 +1850,8 @@ var ( forward_Query_SdkPairing_0 = runtime.ForwardResponseMessage forward_Query_ProvidersEpochCu_0 = runtime.ForwardResponseMessage + + forward_Query_ProviderReputation_0 = runtime.ForwardResponseMessage + + forward_Query_ProviderReputationDetails_0 = runtime.ForwardResponseMessage ) diff --git a/x/pairing/types/relay.pb.go b/x/pairing/types/relay.pb.go index 49f66b8769..e68dd39c6c 100644 --- a/x/pairing/types/relay.pb.go +++ b/x/pairing/types/relay.pb.go @@ -765,9 +765,14 @@ func (m *RelayReply) GetMetadata() []Metadata { } type QualityOfServiceReport struct { - Latency github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=latency,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"latency" yaml:"Latency"` + // Latency of provider answers in milliseconds, range 0-inf, lower is better + Latency github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=latency,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"latency" yaml:"Latency"` + // Percentage of times the provider returned a non-error response, range 0-1, higher is better Availability github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=availability,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"availability" yaml:"availability"` - Sync github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=sync,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"sync" yaml:"sync"` + // 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. + Sync github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=sync,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"sync" yaml:"sync"` } func (m *QualityOfServiceReport) Reset() { *m = QualityOfServiceReport{} } diff --git a/x/pairing/types/reputation.go b/x/pairing/types/reputation.go new file mode 100644 index 0000000000..3e6302712d --- /dev/null +++ b/x/pairing/types/reputation.go @@ -0,0 +1,113 @@ +package types + +import ( + "fmt" + + "cosmossdk.io/collections" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v4/utils" + commontypes "github.com/lavanet/lava/v4/utils/common/types" +) + +var ( + ReputationPrefix = collections.NewPrefix([]byte("Reputation/")) + ReputationPairingScoreBenchmarkStakeThreshold = sdk.NewDecWithPrec(1, 1) // 0.1 = 10% + MaxReputationPairingScore = sdk.NewDec(2) + MinReputationPairingScore = sdk.NewDecWithPrec(5, 1) // 0.5 + DefaultReputationPairingScore = sdk.NewDecWithPrec(125, 2) // 1.25 +) + +// ReputationKey returns a key to the reputations indexed map +func ReputationKey(chainID string, cluster string, provider string) collections.Triple[string, string, string] { + return collections.Join3(chainID, cluster, provider) +} + +func NewReputation(ctx sdk.Context) Reputation { + timestamp := ctx.BlockTime().UTC().Unix() + return Reputation{ + Score: ZeroQosScore, + EpochScore: ZeroQosScore, + TimeLastUpdated: timestamp, + CreationTime: timestamp, + Stake: sdk.NewCoin(commontypes.TokenDenom, sdk.ZeroInt()), + } +} + +func (r Reputation) Equal(other Reputation) bool { + return r.Score.Equal(other.Score) && r.EpochScore.Equal(other.EpochScore) && + r.TimeLastUpdated == other.TimeLastUpdated && r.CreationTime == other.CreationTime && + r.Stake.IsEqual(other.Stake) +} + +// ShouldTruncate checks if the ReputationVarianceStabilizationPeriod has passed since +// the reputation's creation. If so, QoS score reports should be truncated before they're added to the +// reputation's epoch QoS score. +func (r Reputation) ShouldTruncate(stabilizationPeriod int64, currentTime int64) bool { + return r.CreationTime+stabilizationPeriod < currentTime +} + +// calcDecayFactor calculates the appropriate decay factor for a reputation from the time it was last updated +// the decay factor is: exp(-timeSinceLastUpdate/halfLifeFactor) +func (r Reputation) calcDecayFactor(halfLifeFactor int64, currentTime int64) math.LegacyDec { + if halfLifeFactor <= 0 { + utils.LavaFormatWarning("calcDecayFactor: calculate reputation decay factor failed, invalid half life factor", + fmt.Errorf("half life factor is not positive"), + utils.LogAttr("half_life_factor", halfLifeFactor), + ) + return math.LegacyZeroDec() + } + + timeSinceLastUpdate := currentTime - r.TimeLastUpdated + if timeSinceLastUpdate < 0 { + utils.LavaFormatError("calcDecayFactor: calculate reputation decay factor failed, invalid reputation", + fmt.Errorf("reputation last update time is larger than current time"), + utils.LogAttr("current_time", currentTime), + utils.LogAttr("reputation_time_last_updated", r.TimeLastUpdated), + ) + return math.LegacyZeroDec() + } + + // Calculate the decay factor using the natural base e: e^(-timeSinceLastUpdate / halfLifeFactor) + return utils.NaturalBaseExponentFraction(timeSinceLastUpdate, halfLifeFactor, true) +} + +func (r Reputation) ApplyTimeDecayAndUpdateScore(halfLifeFactor int64, currentTime int64) (Reputation, error) { + decayFactor := r.calcDecayFactor(halfLifeFactor, currentTime) + r.Score.Score.Num = (r.Score.Score.Num.Mul(decayFactor)).Add(r.EpochScore.Score.Num) + r.Score.Score.Denom = (r.Score.Score.Denom.Mul(decayFactor)).Add(r.EpochScore.Score.Denom) + r.Score.Variance.Num = (r.Score.Variance.Num.Mul(decayFactor)).Add(r.EpochScore.Variance.Num) + r.Score.Variance.Denom = (r.Score.Variance.Denom.Mul(decayFactor)).Add(r.EpochScore.Variance.Denom) + if !r.Validate() { + return Reputation{}, utils.LavaFormatError("ApplyTimeDecayAndUpdateScore: cannot update reputation", + fmt.Errorf("reputation result is invalid"), + utils.LogAttr("reputation_result", r.String()), + utils.LogAttr("decay_factor", decayFactor.String()), + utils.LogAttr("half_life_factor", halfLifeFactor), + ) + } + return r, nil +} + +func (r Reputation) Validate() bool { + if r.CreationTime <= 0 || r.TimeLastUpdated <= 0 || r.TimeLastUpdated < r.CreationTime || + r.Stake.Denom != commontypes.TokenDenom || !r.Score.Validate() || !r.EpochScore.Validate() { + return false + } + + return true +} + +// ReputationScoreKey returns a key for the reputations fixation store (reputationsFS) +func ReputationScoreKey(chainID string, cluster string, provider string) string { + return chainID + " " + cluster + " " + provider +} + +type ReputationChainClusterKey struct { + ChainID string + Cluster string +} + +func (rck ReputationChainClusterKey) String() string { + return rck.ChainID + " " + rck.Cluster +} diff --git a/x/pairing/types/reputation.pb.go b/x/pairing/types/reputation.pb.go new file mode 100644 index 0000000000..ed49c5eaf0 --- /dev/null +++ b/x/pairing/types/reputation.pb.go @@ -0,0 +1,1133 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: lavanet/lava/pairing/reputation.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Frac is a fracture struct that helps calculating and updating weighted average on the go +type Frac struct { + Num github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=num,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"num" yaml:"num"` + Denom github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=denom,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"denom" yaml:"denom"` +} + +func (m *Frac) Reset() { *m = Frac{} } +func (m *Frac) String() string { return proto.CompactTextString(m) } +func (*Frac) ProtoMessage() {} +func (*Frac) Descriptor() ([]byte, []int) { + return fileDescriptor_38105d47def04072, []int{0} +} +func (m *Frac) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Frac) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Frac.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Frac) XXX_Merge(src proto.Message) { + xxx_messageInfo_Frac.Merge(m, src) +} +func (m *Frac) XXX_Size() int { + return m.Size() +} +func (m *Frac) XXX_DiscardUnknown() { + xxx_messageInfo_Frac.DiscardUnknown(m) +} + +var xxx_messageInfo_Frac proto.InternalMessageInfo + +// 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. +type QosScore struct { + Score Frac `protobuf:"bytes,1,opt,name=score,proto3" json:"score"` + Variance Frac `protobuf:"bytes,2,opt,name=variance,proto3" json:"variance"` +} + +func (m *QosScore) Reset() { *m = QosScore{} } +func (m *QosScore) String() string { return proto.CompactTextString(m) } +func (*QosScore) ProtoMessage() {} +func (*QosScore) Descriptor() ([]byte, []int) { + return fileDescriptor_38105d47def04072, []int{1} +} +func (m *QosScore) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QosScore) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QosScore.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QosScore) XXX_Merge(src proto.Message) { + xxx_messageInfo_QosScore.Merge(m, src) +} +func (m *QosScore) XXX_Size() int { + return m.Size() +} +func (m *QosScore) XXX_DiscardUnknown() { + xxx_messageInfo_QosScore.DiscardUnknown(m) +} + +var xxx_messageInfo_QosScore proto.InternalMessageInfo + +func (m *QosScore) GetScore() Frac { + if m != nil { + return m.Score + } + return Frac{} +} + +func (m *QosScore) GetVariance() Frac { + if m != nil { + return m.Variance + } + return Frac{} +} + +// 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. +type Reputation struct { + Score QosScore `protobuf:"bytes,1,opt,name=score,proto3" json:"score"` + EpochScore QosScore `protobuf:"bytes,2,opt,name=epoch_score,json=epochScore,proto3" json:"epoch_score"` + TimeLastUpdated int64 `protobuf:"varint,3,opt,name=time_last_updated,json=timeLastUpdated,proto3" json:"time_last_updated,omitempty"` + CreationTime int64 `protobuf:"varint,4,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` + Stake types.Coin `protobuf:"bytes,5,opt,name=stake,proto3" json:"stake"` +} + +func (m *Reputation) Reset() { *m = Reputation{} } +func (m *Reputation) String() string { return proto.CompactTextString(m) } +func (*Reputation) ProtoMessage() {} +func (*Reputation) Descriptor() ([]byte, []int) { + return fileDescriptor_38105d47def04072, []int{2} +} +func (m *Reputation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Reputation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Reputation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Reputation) XXX_Merge(src proto.Message) { + xxx_messageInfo_Reputation.Merge(m, src) +} +func (m *Reputation) XXX_Size() int { + return m.Size() +} +func (m *Reputation) XXX_DiscardUnknown() { + xxx_messageInfo_Reputation.DiscardUnknown(m) +} + +var xxx_messageInfo_Reputation proto.InternalMessageInfo + +func (m *Reputation) GetScore() QosScore { + if m != nil { + return m.Score + } + return QosScore{} +} + +func (m *Reputation) GetEpochScore() QosScore { + if m != nil { + return m.EpochScore + } + return QosScore{} +} + +func (m *Reputation) GetTimeLastUpdated() int64 { + if m != nil { + return m.TimeLastUpdated + } + return 0 +} + +func (m *Reputation) GetCreationTime() int64 { + if m != nil { + return m.CreationTime + } + return 0 +} + +func (m *Reputation) GetStake() types.Coin { + if m != nil { + return m.Stake + } + return types.Coin{} +} + +// 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. +type ReputationPairingScore struct { + Score github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=score,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"score" yaml:"score"` +} + +func (m *ReputationPairingScore) Reset() { *m = ReputationPairingScore{} } +func (m *ReputationPairingScore) String() string { return proto.CompactTextString(m) } +func (*ReputationPairingScore) ProtoMessage() {} +func (*ReputationPairingScore) Descriptor() ([]byte, []int) { + return fileDescriptor_38105d47def04072, []int{3} +} +func (m *ReputationPairingScore) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReputationPairingScore) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReputationPairingScore.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReputationPairingScore) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReputationPairingScore.Merge(m, src) +} +func (m *ReputationPairingScore) XXX_Size() int { + return m.Size() +} +func (m *ReputationPairingScore) XXX_DiscardUnknown() { + xxx_messageInfo_ReputationPairingScore.DiscardUnknown(m) +} + +var xxx_messageInfo_ReputationPairingScore proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Frac)(nil), "lavanet.lava.pairing.Frac") + proto.RegisterType((*QosScore)(nil), "lavanet.lava.pairing.QosScore") + proto.RegisterType((*Reputation)(nil), "lavanet.lava.pairing.Reputation") + proto.RegisterType((*ReputationPairingScore)(nil), "lavanet.lava.pairing.ReputationPairingScore") +} + +func init() { + proto.RegisterFile("lavanet/lava/pairing/reputation.proto", fileDescriptor_38105d47def04072) +} + +var fileDescriptor_38105d47def04072 = []byte{ + // 466 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x93, 0xcd, 0x6e, 0x13, 0x31, + 0x10, 0xc7, 0xb3, 0xf9, 0x40, 0x65, 0x5a, 0x84, 0x58, 0x55, 0x68, 0xc9, 0xc1, 0xa9, 0x8c, 0x40, + 0x55, 0x25, 0x6c, 0x95, 0xaf, 0x43, 0x55, 0x71, 0x08, 0x85, 0x13, 0x42, 0xb0, 0x84, 0x0b, 0x97, + 0xc8, 0x71, 0xac, 0xd4, 0x6a, 0x6c, 0xaf, 0xd6, 0xde, 0x88, 0xde, 0x78, 0x04, 0x6e, 0xbc, 0x04, + 0x0f, 0xd2, 0x63, 0x8f, 0x88, 0x43, 0x84, 0x92, 0x37, 0xe0, 0x09, 0x90, 0xed, 0x6d, 0x68, 0xa4, + 0x1e, 0xa8, 0x38, 0x8d, 0x65, 0xff, 0xe7, 0xe7, 0xbf, 0xc7, 0x33, 0xf0, 0x60, 0xca, 0x66, 0x4c, + 0x0b, 0x47, 0x7d, 0xa4, 0x05, 0x93, 0xa5, 0xd4, 0x13, 0x5a, 0x8a, 0xa2, 0x72, 0xcc, 0x49, 0xa3, + 0x49, 0x51, 0x1a, 0x67, 0xd2, 0xed, 0x5a, 0x46, 0x7c, 0x24, 0xb5, 0xac, 0xbb, 0x3d, 0x31, 0x13, + 0x13, 0x04, 0xd4, 0xaf, 0xa2, 0xb6, 0x8b, 0xb8, 0xb1, 0xca, 0x58, 0x3a, 0x62, 0x56, 0xd0, 0xd9, + 0xfe, 0x48, 0x38, 0xb6, 0x4f, 0xb9, 0x91, 0x35, 0x0b, 0x7f, 0x4f, 0xa0, 0xfd, 0xba, 0x64, 0x3c, + 0x7d, 0x0b, 0x2d, 0x5d, 0xa9, 0x2c, 0xd9, 0x49, 0x76, 0x6f, 0xf6, 0x0f, 0xcf, 0xe6, 0xbd, 0xc6, + 0xcf, 0x79, 0xef, 0xe1, 0x44, 0xba, 0xe3, 0x6a, 0x44, 0xb8, 0x51, 0xb4, 0x06, 0xc5, 0xf0, 0xc8, + 0x8e, 0x4f, 0xa8, 0x3b, 0x2d, 0x84, 0x25, 0x47, 0x82, 0xff, 0x9e, 0xf7, 0xe0, 0x94, 0xa9, 0xe9, + 0x01, 0xd6, 0x95, 0xc2, 0xb9, 0x07, 0xa5, 0x03, 0xe8, 0x8c, 0x85, 0x36, 0x2a, 0x6b, 0x06, 0xe2, + 0x8b, 0x6b, 0x13, 0xb7, 0x22, 0x31, 0x40, 0x70, 0x1e, 0x61, 0xf8, 0x4b, 0x02, 0x1b, 0xef, 0x8d, + 0xfd, 0xc0, 0x4d, 0x29, 0xd2, 0xe7, 0xd0, 0xb1, 0x7e, 0x11, 0x4c, 0x6f, 0x3e, 0xee, 0x92, 0xab, + 0xea, 0x42, 0xfc, 0xeb, 0xfa, 0x6d, 0x7f, 0x7d, 0x1e, 0xe5, 0xe9, 0x21, 0x6c, 0xcc, 0x58, 0x29, + 0x99, 0xe6, 0x22, 0xb8, 0xfb, 0x97, 0xd4, 0x55, 0x06, 0xfe, 0xd6, 0x04, 0xc8, 0x57, 0x5f, 0x92, + 0x1e, 0xac, 0x9b, 0x40, 0x57, 0x93, 0x2e, 0x3c, 0xaf, 0x1b, 0x79, 0x05, 0x9b, 0xa2, 0x30, 0xfc, + 0x78, 0x18, 0x09, 0xcd, 0x6b, 0x10, 0x20, 0x24, 0xc6, 0x3a, 0xec, 0xc1, 0x1d, 0x27, 0x95, 0x18, + 0x4e, 0x99, 0x75, 0xc3, 0xaa, 0x18, 0x33, 0x27, 0xc6, 0x59, 0x6b, 0x27, 0xd9, 0x6d, 0xe5, 0xb7, + 0xfd, 0xc1, 0x1b, 0x66, 0xdd, 0xc7, 0xb8, 0x9d, 0xde, 0x87, 0x5b, 0xbc, 0x14, 0xc1, 0xfa, 0xd0, + 0x9f, 0x65, 0xed, 0xa0, 0xdb, 0xba, 0xd8, 0x1c, 0x48, 0x25, 0xd2, 0x67, 0xd0, 0xb1, 0x8e, 0x9d, + 0x88, 0xac, 0x13, 0x1c, 0xdd, 0x23, 0xf1, 0x8b, 0x88, 0x6f, 0x22, 0x52, 0x37, 0x11, 0x79, 0x69, + 0xa4, 0x5e, 0x3d, 0xc7, 0xab, 0xb1, 0x86, 0xbb, 0x7f, 0x0b, 0xf3, 0x2e, 0xfa, 0x8e, 0x0e, 0x07, + 0x97, 0x8b, 0xf4, 0x1f, 0xcd, 0x10, 0x20, 0xb8, 0x2e, 0x5f, 0xff, 0xe8, 0x6c, 0x81, 0x92, 0xf3, + 0x05, 0x4a, 0x7e, 0x2d, 0x50, 0xf2, 0x75, 0x89, 0x1a, 0xe7, 0x4b, 0xd4, 0xf8, 0xb1, 0x44, 0x8d, + 0x4f, 0x7b, 0x97, 0xc0, 0x6b, 0x33, 0x35, 0x7b, 0x4a, 0x3f, 0xaf, 0x06, 0x2b, 0x5c, 0x30, 0xba, + 0x11, 0x06, 0xe1, 0xc9, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x5a, 0xc9, 0x7a, 0x7d, 0x03, + 0x00, 0x00, +} + +func (m *Frac) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Frac) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Frac) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Denom.Size() + i -= size + if _, err := m.Denom.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintReputation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.Num.Size() + i -= size + if _, err := m.Num.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintReputation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QosScore) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QosScore) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QosScore) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Variance.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintReputation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Score.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintReputation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Reputation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Reputation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Reputation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Stake.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintReputation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.CreationTime != 0 { + i = encodeVarintReputation(dAtA, i, uint64(m.CreationTime)) + i-- + dAtA[i] = 0x20 + } + if m.TimeLastUpdated != 0 { + i = encodeVarintReputation(dAtA, i, uint64(m.TimeLastUpdated)) + i-- + dAtA[i] = 0x18 + } + { + size, err := m.EpochScore.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintReputation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Score.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintReputation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ReputationPairingScore) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReputationPairingScore) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReputationPairingScore) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Score.Size() + i -= size + if _, err := m.Score.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintReputation(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintReputation(dAtA []byte, offset int, v uint64) int { + offset -= sovReputation(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Frac) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Num.Size() + n += 1 + l + sovReputation(uint64(l)) + l = m.Denom.Size() + n += 1 + l + sovReputation(uint64(l)) + return n +} + +func (m *QosScore) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Score.Size() + n += 1 + l + sovReputation(uint64(l)) + l = m.Variance.Size() + n += 1 + l + sovReputation(uint64(l)) + return n +} + +func (m *Reputation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Score.Size() + n += 1 + l + sovReputation(uint64(l)) + l = m.EpochScore.Size() + n += 1 + l + sovReputation(uint64(l)) + if m.TimeLastUpdated != 0 { + n += 1 + sovReputation(uint64(m.TimeLastUpdated)) + } + if m.CreationTime != 0 { + n += 1 + sovReputation(uint64(m.CreationTime)) + } + l = m.Stake.Size() + n += 1 + l + sovReputation(uint64(l)) + return n +} + +func (m *ReputationPairingScore) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Score.Size() + n += 1 + l + sovReputation(uint64(l)) + return n +} + +func sovReputation(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozReputation(x uint64) (n int) { + return sovReputation(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Frac) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Frac: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Frac: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Num", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthReputation + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthReputation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Num.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthReputation + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthReputation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Denom.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipReputation(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthReputation + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QosScore) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QosScore: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QosScore: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Score", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthReputation + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthReputation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Score.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Variance", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthReputation + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthReputation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Variance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipReputation(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthReputation + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Reputation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Reputation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Reputation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Score", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthReputation + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthReputation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Score.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EpochScore", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthReputation + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthReputation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.EpochScore.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeLastUpdated", wireType) + } + m.TimeLastUpdated = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeLastUpdated |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CreationTime", wireType) + } + m.CreationTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CreationTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stake", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthReputation + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthReputation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Stake.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipReputation(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthReputation + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReputationPairingScore) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReputationPairingScore: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReputationPairingScore: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Score", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReputation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthReputation + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthReputation + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Score.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipReputation(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthReputation + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipReputation(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowReputation + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowReputation + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowReputation + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthReputation + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupReputation + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthReputation + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthReputation = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowReputation = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupReputation = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/pairing/types/reputation_test.go b/x/pairing/types/reputation_test.go new file mode 100644 index 0000000000..8fe51186fd --- /dev/null +++ b/x/pairing/types/reputation_test.go @@ -0,0 +1,84 @@ +package types + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" +) + +// TestShouldTruncate tests the should truncate method +func TestShouldTruncate(t *testing.T) { + tests := []struct { + name string + creationTime int64 + stabilizationPeriod int64 + currentTime int64 + truncate bool + }{ + { + name: "stabilization time not passed", + creationTime: 1, + stabilizationPeriod: 1, + currentTime: 3, + truncate: true, + }, + { + name: "stabilization time passed", + creationTime: 3, + stabilizationPeriod: 1, + currentTime: 3, + truncate: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reputation := Reputation{CreationTime: tt.creationTime} + require.Equal(t, tt.truncate, reputation.ShouldTruncate(tt.stabilizationPeriod, tt.currentTime)) + }) + } +} + +// TestDecayFactor tests the decay factor method. Note that upon error, the returned decay factor is zero +func TestDecayFactor(t *testing.T) { + tests := []struct { + name string + timeLastUpdated int64 + halfLifeFactor int64 + currentTime int64 + valid bool + }{ + { + name: "happy flow", + timeLastUpdated: 1, + halfLifeFactor: 1, + currentTime: 2, + valid: true, + }, + { + name: "invalid half life factor", + timeLastUpdated: 1, + halfLifeFactor: -1, + currentTime: 2, + valid: false, + }, + { + name: "current time smaller than reputation last update", + timeLastUpdated: 2, + halfLifeFactor: 1, + currentTime: 1, + valid: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reputation := Reputation{TimeLastUpdated: tt.timeLastUpdated} + decay := reputation.calcDecayFactor(tt.halfLifeFactor, tt.currentTime) + if tt.valid { + require.False(t, decay.Equal(math.LegacyZeroDec())) + } else { + require.True(t, decay.Equal(math.LegacyZeroDec())) + } + }) + } +} diff --git a/x/protocol/types/params.go b/x/protocol/types/params.go index 3bb2ddb633..435bb00d46 100644 --- a/x/protocol/types/params.go +++ b/x/protocol/types/params.go @@ -12,8 +12,8 @@ import ( var _ paramtypes.ParamSet = (*Params)(nil) const ( - TARGET_VERSION = "4.2.7" - MIN_VERSION = "3.1.0" + TARGET_VERSION = "4.2.8" + MIN_VERSION = "4.2.1" ) var (