diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 932e04cecb..9de92e14ca 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,14 +12,14 @@ jobs: lint: runs-on: ubuntu-latest steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install Go uses: actions/setup-go@v5 with: go-version: 1.20.5 - - name: Checkout code - uses: actions/checkout@v4 - - name: Lint uses: golangci/golangci-lint-action@v4 with: diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index ad9c6f12c3..95cc07032a 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -23,15 +23,15 @@ jobs: # with: # input: "proto" - break-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.38.0 - - uses: bufbuild/buf-breaking-action@v1 - with: - input: "proto" - against: "https://github.com/${{ github.repository }}.git#branch=${{ github.event.pull_request.base.ref }},ref=HEAD~1,subdir=proto" + # break-check: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v4 + # - uses: bufbuild/buf-setup-action@v1.38.0 + # - uses: bufbuild/buf-breaking-action@v1 + # with: + # input: "proto" + # against: "https://github.com/${{ github.repository }}.git#branch=${{ github.event.pull_request.base.ref }},ref=HEAD~1,subdir=proto" validate-check: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4f5810d141..6cb7c483a5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Publish Lava Release on: push: tags: - - 'v[0-9]+.[0-9]+.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+(-[a-zA-Z0-9]+)?' workflow_dispatch: inputs: release_tag: diff --git a/app/app.go b/app/app.go index 7297ef3b99..54dfedb250 100644 --- a/app/app.go +++ b/app/app.go @@ -166,42 +166,6 @@ const ( // Upgrades add here future upgrades (upgrades.Upgrade) var Upgrades = []upgrades.Upgrade{ - upgrades.Upgrade_0_22_0, - upgrades.Upgrade_0_23_0, - upgrades.Upgrade_0_23_2, - upgrades.Upgrade_0_23_4, - upgrades.Upgrade_0_23_5, - upgrades.Upgrade_0_24_0, - upgrades.Upgrade_0_25_0, - upgrades.Upgrade_0_25_1, - upgrades.Upgrade_0_25_2, - upgrades.Upgrade_0_26_0, - upgrades.Upgrade_0_26_1, - upgrades.Upgrade_0_26_2, - upgrades.Upgrade_0_27_0, - upgrades.Upgrade_0_30_0, - upgrades.Upgrade_0_30_1, - upgrades.Upgrade_0_30_2, - upgrades.Upgrade_0_31_0, - upgrades.Upgrade_0_31_1, - upgrades.Upgrade_0_32_0, - upgrades.Upgrade_0_32_3, - upgrades.Upgrade_0_33_0, - upgrades.Upgrade_0_34_0, - upgrades.Upgrade_0_35_0, - upgrades.Upgrade_1_0_0, - upgrades.Upgrade_1_0_1, - upgrades.Upgrade_1_1_0, - upgrades.Upgrade_1_2_0, - upgrades.Upgrade_2_0_0, - upgrades.Upgrade_2_1_0, - upgrades.Upgrade_2_1_1, - upgrades.Upgrade_2_1_3, - upgrades.Upgrade_2_2_0, - upgrades.Upgrade_2_4_0, - upgrades.Upgrade_2_5_0, - upgrades.Upgrade_3_0_0, - upgrades.Upgrade_3_0_1, upgrades.Upgrade_3_1_0, } diff --git a/app/upgrades/empty_upgrades.go b/app/upgrades/empty_upgrades.go index 348fd31622..1b18de859a 100644 --- a/app/upgrades/empty_upgrades.go +++ b/app/upgrades/empty_upgrades.go @@ -4,19 +4,8 @@ import ( store "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" - "github.com/cosmos/cosmos-sdk/x/group" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/types" - icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" - icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" "github.com/lavanet/lava/v3/app/keepers" - v1 "github.com/lavanet/lava/v3/x/downtime/v1" - dualstakingtypes "github.com/lavanet/lava/v3/x/dualstaking/types" - fixationtypes "github.com/lavanet/lava/v3/x/fixationstore/types" - protocoltypes "github.com/lavanet/lava/v3/x/protocol/types" - rewardstypes "github.com/lavanet/lava/v3/x/rewards/types" ) func defaultUpgradeHandler( @@ -45,256 +34,6 @@ func defaultUpgradeHandler( // }, // } -var Upgrade_0_22_0 = Upgrade{ - UpgradeName: "v0.22.0", - CreateUpgradeHandler: v0_22_0_UpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -func v0_22_0_UpgradeHandler( - m *module.Manager, - c module.Configurator, - bapm BaseAppParamManager, - lk *keepers.LavaKeepers, -) upgradetypes.UpgradeHandler { - return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { - lk.DowntimeKeeper.SetParams(ctx, v1.DefaultParams()) - lk.ProtocolKeeper.SetParams(ctx, protocoltypes.DefaultParams()) - return m.RunMigrations(ctx, c, vm) - } -} - -var Upgrade_0_23_0 = Upgrade{ - UpgradeName: "v0.23.0", - CreateUpgradeHandler: v0_23_0_UpgradeHandler, - StoreUpgrades: store.StoreUpgrades{Added: []string{dualstakingtypes.StoreKey}}, -} - -func v0_23_0_UpgradeHandler( - m *module.Manager, - c module.Configurator, - bapm BaseAppParamManager, - lk *keepers.LavaKeepers, -) upgradetypes.UpgradeHandler { - return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { - lk.PairingKeeper.InitProviderQoS(ctx, *fixationtypes.DefaultGenesis()) - return m.RunMigrations(ctx, c, vm) - } -} - -var Upgrade_0_23_2 = Upgrade{ - UpgradeName: "v0.23.2", // upgrade name - CreateUpgradeHandler: defaultUpgradeHandler, // upgrade handler (default) - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_23_4 = Upgrade{ - UpgradeName: "v0.23.4", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_23_5 = Upgrade{ - UpgradeName: "v0.23.5", - CreateUpgradeHandler: v0_23_0_UpgradeHandler, - StoreUpgrades: store.StoreUpgrades{Added: []string{dualstakingtypes.StoreKey}}, -} - -var Upgrade_0_24_0 = Upgrade{ - UpgradeName: "v0.24.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_25_0 = Upgrade{ - UpgradeName: "v0.25.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_25_1 = Upgrade{ - UpgradeName: "v0.25.1", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_25_2 = Upgrade{ - UpgradeName: "v0.25.2", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_26_0 = Upgrade{ - UpgradeName: "v0.26.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_26_1 = Upgrade{ - UpgradeName: "v0.26.1", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_26_2 = Upgrade{ - UpgradeName: "v0.26.2", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_27_0 = Upgrade{ - UpgradeName: "v0.27.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_30_0 = Upgrade{ - UpgradeName: "v0.30.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_30_1 = Upgrade{ - UpgradeName: "v0.30.1", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_30_2 = Upgrade{ - UpgradeName: "v0.30.2", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_31_0 = Upgrade{ - UpgradeName: "v0.31.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_31_1 = Upgrade{ - UpgradeName: "v0.31.1", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_32_0 = Upgrade{ - UpgradeName: "v0.32.0", - CreateUpgradeHandler: v0_32_0_UpgradeHandler, - StoreUpgrades: store.StoreUpgrades{ - Added: []string{rewardstypes.StoreKey}, - Deleted: []string{minttypes.StoreKey}, - }, -} - -var Upgrade_0_32_3 = Upgrade{ - UpgradeName: "v0.32.3", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_33_0 = Upgrade{ - UpgradeName: "v0.33.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_0_34_0 = Upgrade{ - UpgradeName: "v0.34.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{ - Added: []string{icahosttypes.StoreKey, icacontrollertypes.StoreKey}, - }, -} - -var Upgrade_0_35_0 = Upgrade{ - UpgradeName: "v0.35.0", - CreateUpgradeHandler: v_35_0, - StoreUpgrades: store.StoreUpgrades{Added: []string{authzkeeper.StoreKey, group.StoreKey}}, -} - -var Upgrade_1_0_0 = Upgrade{ - UpgradeName: "v1.0.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_1_0_1 = Upgrade{ - UpgradeName: "v1.0.1", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_1_1_0 = Upgrade{ - UpgradeName: "v1.1.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_1_2_0 = Upgrade{ - UpgradeName: "v1.2.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_2_0_0 = Upgrade{ - UpgradeName: "v2.0.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{ - Added: []string{packetforwardtypes.StoreKey}, - }, -} - -var Upgrade_2_1_0 = Upgrade{ - UpgradeName: "v2.1.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_2_1_1 = Upgrade{ - UpgradeName: "v2.1.1", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_2_1_3 = Upgrade{ - UpgradeName: "v2.1.3", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_2_2_0 = Upgrade{ - UpgradeName: "v2.2.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{ - Added: []string{packetforwardtypes.StoreKey}, - }, -} - -var Upgrade_2_4_0 = Upgrade{ - UpgradeName: "v2.4.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_2_5_0 = Upgrade{ - UpgradeName: "v2.5.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_3_0_0 = Upgrade{ - UpgradeName: "v3.0.0", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - -var Upgrade_3_0_1 = Upgrade{ - UpgradeName: "v3.0.1", - CreateUpgradeHandler: defaultUpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, -} - var Upgrade_3_1_0 = Upgrade{ UpgradeName: "v3.1.0", CreateUpgradeHandler: defaultUpgradeHandler, diff --git a/app/upgrades/upgrade_0_32_0.go b/app/upgrades/upgrade_0_32_0.go deleted file mode 100644 index 79eb7971c8..0000000000 --- a/app/upgrades/upgrade_0_32_0.go +++ /dev/null @@ -1,69 +0,0 @@ -package upgrades - -import ( - "fmt" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/bank/types" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/lavanet/lava/v3/app/keepers" - dualstakingtypes "github.com/lavanet/lava/v3/x/dualstaking/types" - rewardstypes "github.com/lavanet/lava/v3/x/rewards/types" -) - -func v0_32_0_UpgradeHandler( - m *module.Manager, - c module.Configurator, - bapm BaseAppParamManager, - lk *keepers.LavaKeepers, -) upgradetypes.UpgradeHandler { - return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { - // rewards pool - totalSupply, err := lk.BankKeeper.TotalSupply(ctx, &types.QueryTotalSupplyRequest{}) - if err != nil { - return nil, err - } - allocate := totalSupply.Supply.QuoInt(sdk.NewIntFromUint64(33)) - err = lk.BankKeeper.MintCoins(ctx, string(rewardstypes.ValidatorsRewardsAllocationPoolName), allocate) - if err != nil { - return nil, err - } - err = lk.BankKeeper.MintCoins(ctx, string(rewardstypes.ProvidersRewardsAllocationPool), allocate) - if err != nil { - return nil, err - } - - rewards := lk.DualstakingKeeper.GetAllDelegatorReward(ctx) - for _, reward := range rewards { - lk.DualstakingKeeper.RemoveDelegatorReward(ctx, dualstakingtypes.DelegationKey(reward.Provider, reward.Delegator, reward.ChainId)) - } - rewards = lk.DualstakingKeeper.GetAllDelegatorReward(ctx) - if len(rewards) != 0 { - return nil, fmt.Errorf("v0.32.0 UpgradeHandler: did not delete all rewards") - } - - // expedited proposal - // deposit - params := lk.GovKeeper.GetParams(ctx) - params.ExpeditedMinDeposit = append(params.ExpeditedMinDeposit, params.MinDeposit[0].AddAmount(sdk.NewIntFromUint64(1000))) - - // tally - params.ExpeditedThreshold = "0.75" - - // voting - seconds := params.VotingPeriod.Nanoseconds() - duration := time.Duration(seconds/10) * time.Nanosecond - params.ExpeditedVotingPeriod = &duration - - if err = params.ValidateBasic(); err != nil { - return nil, err - } - - if err := lk.GovKeeper.SetParams(ctx, params); err != nil { - return nil, err - } - return m.RunMigrations(ctx, c, vm) - } -} diff --git a/cookbook/specs/axelar.json b/cookbook/specs/axelar.json index efe2a590ea..f3ff6211d4 100644 --- a/cookbook/specs/axelar.json +++ b/cookbook/specs/axelar.json @@ -15,7 +15,7 @@ "data_reliability_enabled": true, "block_distance_for_finalized_data": 0, "blocks_in_finalization_proof": 1, - "average_block_time": 5000, + "average_block_time": 5800, "allowed_block_lag_for_qos_sync": 2, "shares": 1, "min_stake_provider": { diff --git a/cookbook/specs/evmos.json b/cookbook/specs/evmos.json index 0ea24db338..c0324f3cae 100644 --- a/cookbook/specs/evmos.json +++ b/cookbook/specs/evmos.json @@ -1560,7 +1560,7 @@ "name": "pruning", "parse_directive": { "function_tag": "GET_BLOCK_BY_NUM" - }, + }, "values": [ { "latest_distance": 10000 @@ -1581,6 +1581,74 @@ } } ] + }, + { + "enabled": true, + "collection_data": { + "api_interface": "jsonrpc", + "internal_path": "", + "type": "POST", + "add_on": "debug" + }, + "apis": [ + { + "name": "debug_getBadBlocks", + "compute_units": 1, + "enabled": false + }, + { + "name": "debug_getRawBlock", + "compute_units": 1, + "enabled": false + }, + { + "name": "debug_getRawHeader", + "compute_units": 1, + "enabled": false + }, + { + "name": "debug_getRawReceipts", + "compute_units": 1, + "enabled": false + }, + { + "name": "debug_getRawTransaction", + "compute_units": 1, + "enabled": false + }, + { + "name": "debug_storageRangeAt", + "compute_units": 1, + "enabled": false + }, + { + "name": "debug_traceCall", + "compute_units": 1, + "enabled": false + } + ], + "verifications": [ + { + "name": "enabled", + "parse_directive": { + "function_template": "{\"jsonrpc\":\"2.0\",\"method\":\"debug_traceBlockByNumber\",\"params\":[\"latest\", {\"tracer\": \"callTracer\"}],\"id\":1}", + "function_tag": "VERIFICATION", + "result_parsing": { + "parser_arg": [ + "0" + ], + "parser_func": "PARSE_BY_ARG", + "encoding": "hex" + }, + "api_name": "debug_traceBlockByNumber" + }, + "values": [ + { + "expected_value": "*" + } + ] + } + ] } ] }, diff --git a/cookbook/specs/solana.json b/cookbook/specs/solana.json index 74c727a894..e14203dd39 100644 --- a/cookbook/specs/solana.json +++ b/cookbook/specs/solana.json @@ -1196,6 +1196,27 @@ "expected_value": "*" } ] + }, + { + "name": "tokens-owner-indexed", + "parse_directive": { + "function_template": "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"getTokenAccountsByOwner\",\"params\":[\"4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F\",{\"programId\":\"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\"},{\"encoding\":\"jsonParsed\"}]}", + "function_tag": "VERIFICATION", + "result_parsing": { + "parser_arg": [ + "0", + "value" + ], + "parser_func": "PARSE_CANONICAL" + }, + "api_name": "getTokenAccountsByOwner" + }, + "values": [ + { + "expected_value": "*", + "severity": "Warning" + } + ] } ] } diff --git a/cookbook/specs/ton.json b/cookbook/specs/ton.json new file mode 100644 index 0000000000..33800d77f8 --- /dev/null +++ b/cookbook/specs/ton.json @@ -0,0 +1,538 @@ +{ + "proposal": { + "title": "Add Specs: Ton", + "description": "Adding new specification support for relaying Ton data on Lava", + "specs": [ + { + "index": "TON", + "name": "ton mainnet", + "enabled": true, + "reliability_threshold": 268435455, + "data_reliability_enabled": true, + "block_distance_for_finalized_data": 2, + "blocks_in_finalization_proof": 3, + "average_block_time": 4500, + "allowed_block_lag_for_qos_sync": 3, + "shares": 1, + "min_stake_provider": { + "denom": "ulava", + "amount": "47500000000" + }, + "api_collections": [ + { + "enabled": true, + "collection_data": { + "api_interface": "rest", + "internal_path": "", + "type": "GET", + "add_on": "" + }, + "apis": [ + { + "name": "/api/v3/masterchainInfo", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/blocks", + "block_parsing": { + "parser_arg": [ + "0", + "seqno" + ], + "parser_func": "PARSE_CANONICAL" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0, + "parsers": [ + { + "parse_path": ".params.seqno", + "parse_type": "BLOCK_LATEST" + } + ] + }, + { + "name": "/api/v3/masterchainBlockShardState", + "block_parsing": { + "parser_arg": [ + "0", + "seqno" + ], + "parser_func": "PARSE_CANONICAL" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0, + "parsers": [ + { + "parse_path": ".params.seqno", + "parse_type": "BLOCK_LATEST" + } + ] + }, + { + "name": "/api/v3/addressBook", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/masterchainBlockShards", + "block_parsing": { + "parser_arg": [ + "0", + "seqno" + ], + "parser_func": "PARSE_CANONICAL" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0, + "parsers": [ + { + "parse_path": ".params.seqno", + "parse_type": "BLOCK_LATEST" + } + ] + }, + { + "name": "/api/v3/transactions", + "block_parsing": { + "parser_arg": [ + "0", + "seqno" + ], + "parser_func": "PARSE_CANONICAL" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0, + "parsers": [ + { + "parse_path": ".params.seqno", + "parse_type": "BLOCK_LATEST" + } + ] + }, + { + "name": "/api/v3/transactionsByMasterchainBlock", + "block_parsing": { + "parser_arg": [ + "0", + "seqno" + ], + "parser_func": "PARSE_CANONICAL" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0, + "parsers": [ + { + "parse_path": ".params.seqno", + "parse_type": "BLOCK_LATEST" + } + ] + }, + { + "name": "/api/v3/transactionsByMessage", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/adjacentTransactions", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/messages", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/nft/collections", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/nft/items", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/nft/transfers", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/jetton/masters", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/jetton/wallets", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/jetton/transfers", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/jetton/burns", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/account", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/wallet", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + } + ], + "headers": [], + "inheritance_apis": [], + "parse_directives": [ + { + "function_template": "/api/v3/masterchainInfo", + "function_tag": "GET_BLOCKNUM", + "result_parsing": { + "parser_arg": [ + "0", + "last", + "seqno" + ], + "parser_func": "PARSE_CANONICAL" + }, + "api_name": "/api/v3/masterchainInfo" + }, + { + "function_template": "/api/v3/blocks?workchain=-1&shard=8000000000000000&seqno=%d", + "function_tag": "GET_BLOCK_BY_NUM", + "result_parsing": { + "parser_arg": [ + "0", + "blocks", + "0", + "seqno" + ], + "parser_func": "PARSE_CANONICAL" + }, + "api_name": "/api/v3/blocks" + } + ], + "verifications": [ + { + "name": "chain-id", + "parse_directive": { + "function_template": "/api/v3/masterchainInfo", + "function_tag": "VERIFICATION", + "result_parsing": { + "parser_arg": [ + "0" + ], + "parser_func": "PARSE_CANONICAL" + }, + "api_name": "/api/v3/masterchainInfo" + }, + "values": [ + { + "expected_value": "*" + } + ] + } + ], + "extensions": [] + }, + { + "enabled": true, + "collection_data": { + "api_interface": "rest", + "internal_path": "", + "type": "POST", + "add_on": "" + }, + "apis": [ + { + "name": "/api/v3/message", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 1 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/runGetMethod", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + }, + { + "name": "/api/v3/estimateFee", + "block_parsing": { + "parser_arg": [ + "latest" + ], + "parser_func": "DEFAULT" + }, + "compute_units": 10, + "enabled": true, + "category": { + "deterministic": true, + "local": false, + "subscription": false, + "stateful": 0 + }, + "extra_compute_units": 0 + } + ], + "headers": [], + "inheritance_apis": [], + "parse_directives": [], + "verifications": [], + "extensions": [] + } + ] + } + ] + }, + "deposit": "10000000ulava" +} \ No newline at end of file diff --git a/ecosystem/cache/server.go b/ecosystem/cache/server.go index 9f3f8df1dc..fd867b303c 100644 --- a/ecosystem/cache/server.go +++ b/ecosystem/cache/server.go @@ -107,7 +107,7 @@ func (cs *CacheServer) Serve(ctx context.Context, if strings.HasPrefix(listenAddr, unixPrefix) { // Unix socket host, port, err := net.SplitHostPort(listenAddr) if err != nil { - utils.LavaFormatFatal("Failed to parse unix socket, provide address in this format unix:/tmp/example.sock: %v\n", err) + utils.LavaFormatFatal("Failed to parse unix socket, provide address in this format unix:/tmp/example.sock", err) return } @@ -115,26 +115,26 @@ func (cs *CacheServer) Serve(ctx context.Context, addr, err := net.ResolveUnixAddr(host, port) if err != nil { - utils.LavaFormatFatal("Failed to resolve unix socket address: %v\n", err) + utils.LavaFormatFatal("Failed to resolve unix socket address", err) return } lis, err = net.ListenUnix(host, addr) if err != nil { - utils.LavaFormatFatal("Faild to listen to unix socket listener: %v\n", err) + utils.LavaFormatFatal("Failed to listen to unix socket listener", err) return } // Set permissions for the Unix socket err = os.Chmod(port, 0o600) if err != nil { - utils.LavaFormatFatal("Failed to set permissions for Unix socket: %v\n", err) + utils.LavaFormatFatal("Failed to set permissions for Unix socket", err) return } } else { lis, err = net.Listen("tcp", listenAddr) if err != nil { - utils.LavaFormatFatal("Cache server failure setting up TCP listener: %v\n", err) + utils.LavaFormatFatal("Cache server failure setting up TCP listener", err) return } } diff --git a/go.mod b/go.mod index 67e086b80a..82fc25f95b 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/fullstorydev/grpcurl v1.8.5 github.com/goccy/go-json v0.10.2 github.com/gogo/status v1.1.0 + github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 github.com/itchyny/gojq v0.12.16 github.com/jhump/protoreflect v1.15.1 @@ -85,7 +86,6 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/golang/glog v1.2.0 // indirect - github.com/golang/mock v1.6.0 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/s2a-go v0.1.7 // indirect diff --git a/proto/lavanet/lava/dualstaking/delegate.proto b/proto/lavanet/lava/dualstaking/delegate.proto index ea0d3986f1..8926254f89 100644 --- a/proto/lavanet/lava/dualstaking/delegate.proto +++ b/proto/lavanet/lava/dualstaking/delegate.proto @@ -7,8 +7,8 @@ import "gogoproto/gogo.proto"; import "cosmos/base/v1beta1/coin.proto"; message Delegation { + reserved 2; string provider = 1; // provider receives the delegated funds - string chainID = 2; // chainID to which staking delegate funds string delegator = 3; // delegator that owns the delegated funds cosmos.base.v1beta1.Coin amount = 4 [(gogoproto.nullable) = false]; int64 timestamp = 5; // Unix timestamp of the delegation (+ month) diff --git a/proto/lavanet/lava/dualstaking/delegator_reward.proto b/proto/lavanet/lava/dualstaking/delegator_reward.proto index 1145cf685e..c48be80aa6 100644 --- a/proto/lavanet/lava/dualstaking/delegator_reward.proto +++ b/proto/lavanet/lava/dualstaking/delegator_reward.proto @@ -7,9 +7,9 @@ import "gogoproto/gogo.proto"; import "cosmos/base/v1beta1/coin.proto"; message DelegatorReward { + reserved 3; string delegator = 1; string provider = 2; - string chain_id = 3; repeated cosmos.base.v1beta1.Coin amount = 4 [ (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false diff --git a/proto/lavanet/lava/dualstaking/genesis.proto b/proto/lavanet/lava/dualstaking/genesis.proto index b6a97c3491..1c37aac064 100644 --- a/proto/lavanet/lava/dualstaking/genesis.proto +++ b/proto/lavanet/lava/dualstaking/genesis.proto @@ -3,17 +3,15 @@ package lavanet.lava.dualstaking; import "gogoproto/gogo.proto"; import "lavanet/lava/dualstaking/params.proto"; -import "lavanet/lava/fixationstore/fixation.proto"; -import "lavanet/lava/timerstore/timer.proto"; import "lavanet/lava/dualstaking/delegator_reward.proto"; +import "lavanet/lava/dualstaking/delegate.proto"; option go_package = "github.com/lavanet/lava/v3/x/dualstaking/types"; // GenesisState defines the dualstaking module's genesis state. message GenesisState { Params params = 1 [(gogoproto.nullable) = false]; - lavanet.lava.fixationstore.GenesisState delegationsFS = 2 [(gogoproto.nullable) = false]; - lavanet.lava.fixationstore.GenesisState delegatorsFS = 3 [(gogoproto.nullable) = false]; - reserved 4; + reserved 2,3,4; repeated DelegatorReward delegator_reward_list = 5 [(gogoproto.nullable) = false]; + repeated Delegation Delegations = 6 [(gogoproto.nullable) = false]; } diff --git a/proto/lavanet/lava/dualstaking/query.proto b/proto/lavanet/lava/dualstaking/query.proto index 3cb0941678..a0892a84f3 100644 --- a/proto/lavanet/lava/dualstaking/query.proto +++ b/proto/lavanet/lava/dualstaking/query.proto @@ -76,8 +76,8 @@ message QueryDelegatorRewardsResponse { } message DelegatorRewardInfo { + reserved 2; string provider = 1; - string chain_id = 2; repeated cosmos.base.v1beta1.Coin amount = 3 [ (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false diff --git a/proto/lavanet/lava/epochstorage/provider_metadata.proto b/proto/lavanet/lava/epochstorage/provider_metadata.proto new file mode 100644 index 0000000000..449c3989ac --- /dev/null +++ b/proto/lavanet/lava/epochstorage/provider_metadata.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package lavanet.lava.epochstorage; + +option go_package = "github.com/lavanet/lava/v3/x/epochstorage/types"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "amino/amino.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +message ProviderMetadata { + string provider = 1; + string vault = 2; + cosmos.base.v1beta1.Coin total_delegations = 3 [(gogoproto.nullable) = false]; + repeated string chains = 4; + uint64 delegate_commission = 5; // delegation commission (precentage 0-100) + uint64 last_change = 6; + cosmos.staking.v1beta1.Description description = 7 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; +} \ No newline at end of file diff --git a/proto/lavanet/lava/epochstorage/query.proto b/proto/lavanet/lava/epochstorage/query.proto index 402a498fe3..8598ce9ec0 100644 --- a/proto/lavanet/lava/epochstorage/query.proto +++ b/proto/lavanet/lava/epochstorage/query.proto @@ -7,6 +7,7 @@ import "lavanet/lava/epochstorage/params.proto"; import "lavanet/lava/epochstorage/stake_storage.proto"; import "lavanet/lava/epochstorage/epoch_details.proto"; import "lavanet/lava/epochstorage/fixated_params.proto"; +import "lavanet/lava/epochstorage/provider_metadata.proto"; // this line is used by starport scaffolding # 1 import "cosmos/base/v1beta1/coin.proto"; import "gogoproto/gogo.proto"; @@ -44,6 +45,11 @@ service Query { option (google.api.http).get = "/lavanet/lava/epochstorage/fixated_params"; } + // Queries provider metadata. + rpc ProviderMetaData(QueryProviderMetaDataRequest) returns (QueryProviderMetaDataResponse) { + option (google.api.http).get = "/lavanet/lava/epochstorage/provider_metadata/{provider}"; + } + // this line is used by starport scaffolding # 2 } @@ -98,4 +104,12 @@ message QueryAllFixatedParamsResponse { cosmos.base.query.v1beta1.PageResponse pagination = 2; } +message QueryProviderMetaDataRequest { + string provider = 1; +} + +message QueryProviderMetaDataResponse { + repeated ProviderMetadata MetaData = 1 [(gogoproto.nullable) = false]; +} + // this line is used by starport scaffolding # 3 diff --git a/proto/lavanet/lava/epochstorage/stake_entry.proto b/proto/lavanet/lava/epochstorage/stake_entry.proto index 89b832d854..999ab14af8 100644 --- a/proto/lavanet/lava/epochstorage/stake_entry.proto +++ b/proto/lavanet/lava/epochstorage/stake_entry.proto @@ -9,7 +9,7 @@ import "cosmos/staking/v1beta1/staking.proto"; import "amino/amino.proto"; message StakeEntry { - reserved 7,10; + reserved 7,10,12; cosmos.base.v1beta1.Coin stake = 1 [(gogoproto.nullable) = false]; string address = 2; uint64 stake_applied_block = 3; @@ -19,7 +19,6 @@ message StakeEntry { string moniker = 8; cosmos.base.v1beta1.Coin delegate_total = 9 [(gogoproto.nullable) = false]; // delegation total uint64 delegate_commission = 11; // delegation commission (precentage 0-100) - uint64 last_change = 12; BlockReport block_report = 13; string vault = 14; cosmos.staking.v1beta1.Description description = 15 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; diff --git a/proto/lavanet/lava/pairing/tx.proto b/proto/lavanet/lava/pairing/tx.proto index 84adcbe410..987f3805ab 100644 --- a/proto/lavanet/lava/pairing/tx.proto +++ b/proto/lavanet/lava/pairing/tx.proto @@ -17,6 +17,7 @@ service Msg { rpc RelayPayment(MsgRelayPayment) returns (MsgRelayPaymentResponse); rpc FreezeProvider(MsgFreezeProvider) returns (MsgFreezeProviderResponse); rpc UnfreezeProvider(MsgUnfreezeProvider) returns (MsgUnfreezeProviderResponse); + rpc MoveProviderStake(MsgMoveProviderStake) returns (MsgMoveProviderStakeResponse); // this line is used by starport scaffolding # proto/tx/rpc } @@ -85,4 +86,16 @@ message MsgUnfreezeProvider { message MsgUnfreezeProviderResponse { } + +message MsgMoveProviderStake { + option (amino.name) = "pairing/MoveProviderStake"; + string creator = 1; + string src_chain =2; + string dst_chain =3; + cosmos.base.v1beta1.Coin amount = 4 [(gogoproto.nullable) = false]; +} + +message MsgMoveProviderStakeResponse { +} + // this line is used by starport scaffolding # proto/tx/message \ No newline at end of file diff --git a/proto/lavanet/lava/spec/api_collection.proto b/proto/lavanet/lava/spec/api_collection.proto index 4f9c7434e1..27750346b6 100644 --- a/proto/lavanet/lava/spec/api_collection.proto +++ b/proto/lavanet/lava/spec/api_collection.proto @@ -83,6 +83,7 @@ message ParseDirective { string function_template = 2; BlockParser result_parsing = 3 [(gogoproto.nullable) = false]; string api_name = 4; + repeated GenericParser parsers = 5 [(gogoproto.nullable) = false]; } message BlockParser { diff --git a/protocol/chainlib/chain_fetcher.go b/protocol/chainlib/chain_fetcher.go index 565ace6948..9a5d6c421b 100644 --- a/protocol/chainlib/chain_fetcher.go +++ b/protocol/chainlib/chain_fetcher.go @@ -188,68 +188,71 @@ func (cf *ChainFetcher) Verify(ctx context.Context, verification VerificationCon ) } - parsedResult, err := parser.ParseFromReply(parserInput, parsing.ResultParsing) - if err != nil { - return utils.LavaFormatWarning("[-] verify failed to parse result", err, []utils.Attribute{ - {Key: "chainId", Value: chainId}, - {Key: "nodeUrl", Value: proxyUrl.Url}, - {Key: "Method", Value: parsing.GetApiName()}, - {Key: "Response", Value: string(reply.RelayReply.Data)}, - }...) + parsedInput := parser.ParseBlockFromReply(parserInput, parsing.ResultParsing, parsing.Parsers) + if parsedInput.GetRawParsedData() == "" { + return utils.LavaFormatWarning("[-] verify failed to parse result", err, + utils.LogAttr("chainId", chainId), + utils.LogAttr("nodeUrl", proxyUrl.Url), + utils.LogAttr("Method", parsing.GetApiName()), + utils.LogAttr("Response", string(reply.RelayReply.Data)), + ) } if verification.LatestDistance != 0 && latestBlock != 0 && verification.ParseDirective.FunctionTag != spectypes.FUNCTION_TAG_GET_BLOCK_BY_NUM { - parsedResultAsNumber, err := strconv.ParseUint(parsedResult, 0, 64) - if err != nil { - return utils.LavaFormatWarning("[-] verify failed to parse result as number", err, []utils.Attribute{ - {Key: "chainId", Value: chainId}, - {Key: "nodeUrl", Value: proxyUrl.Url}, - {Key: "Method", Value: parsing.GetApiName()}, - {Key: "Response", Value: string(reply.RelayReply.Data)}, - {Key: "parsedResult", Value: parsedResult}, - }...) + parsedResultAsNumber := parsedInput.GetBlock() + if parsedResultAsNumber == spectypes.NOT_APPLICABLE { + return utils.LavaFormatWarning("[-] verify failed to parse result as number", err, + utils.LogAttr("chainId", chainId), + utils.LogAttr("nodeUrl", proxyUrl.Url), + utils.LogAttr("Method", parsing.GetApiName()), + utils.LogAttr("Response", string(reply.RelayReply.Data)), + utils.LogAttr("rawParsedData", parsedInput.GetRawParsedData()), + ) } - if parsedResultAsNumber > latestBlock { - return utils.LavaFormatWarning("[-] verify failed parsed result is greater than latestBlock", err, []utils.Attribute{ - {Key: "chainId", Value: chainId}, - {Key: "nodeUrl", Value: proxyUrl.Url}, - {Key: "Method", Value: parsing.GetApiName()}, - {Key: "latestBlock", Value: latestBlock}, - {Key: "parsedResult", Value: parsedResultAsNumber}, - }...) + uint64ParsedResultAsNumber := uint64(parsedResultAsNumber) + if uint64ParsedResultAsNumber > latestBlock { + return utils.LavaFormatWarning("[-] verify failed parsed result is greater than latestBlock", err, + utils.LogAttr("chainId", chainId), + utils.LogAttr("nodeUrl", proxyUrl.Url), + utils.LogAttr("Method", parsing.GetApiName()), + utils.LogAttr("latestBlock", latestBlock), + utils.LogAttr("parsedResult", uint64ParsedResultAsNumber), + ) } - if latestBlock-parsedResultAsNumber < verification.LatestDistance { - return utils.LavaFormatWarning("[-] verify failed expected block distance is not sufficient", err, []utils.Attribute{ - {Key: "chainId", Value: chainId}, - {Key: "nodeUrl", Value: proxyUrl.Url}, - {Key: "Method", Value: parsing.GetApiName()}, - {Key: "latestBlock", Value: latestBlock}, - {Key: "parsedResult", Value: parsedResultAsNumber}, - {Key: "expected", Value: verification.LatestDistance}, - }...) + if latestBlock-uint64ParsedResultAsNumber < verification.LatestDistance { + return utils.LavaFormatWarning("[-] verify failed expected block distance is not sufficient", err, + utils.LogAttr("chainId", chainId), + utils.LogAttr("nodeUrl", proxyUrl.Url), + utils.LogAttr("Method", parsing.GetApiName()), + utils.LogAttr("latestBlock", latestBlock), + utils.LogAttr("parsedResult", uint64ParsedResultAsNumber), + utils.LogAttr("expected", verification.LatestDistance), + ) } } // some verifications only want the response to be valid, and don't care about the value if verification.Value != "*" && verification.Value != "" && verification.ParseDirective.FunctionTag != spectypes.FUNCTION_TAG_GET_BLOCK_BY_NUM { - if parsedResult != verification.Value { - return utils.LavaFormatWarning("[-] verify failed expected and received are different", err, []utils.Attribute{ - {Key: "chainId", Value: chainId}, - {Key: "nodeUrl", Value: proxyUrl.Url}, - {Key: "parsedResult", Value: parsedResult}, - {Key: "verification.Value", Value: verification.Value}, - {Key: "Method", Value: parsing.GetApiName()}, - {Key: "Extension", Value: verification.Extension}, - {Key: "Addon", Value: verification.Addon}, - {Key: "Verification", Value: verification.Name}, - }...) + rawData := parsedInput.GetRawParsedData() + if rawData != verification.Value { + return utils.LavaFormatWarning("[-] verify failed expected and received are different", err, + utils.LogAttr("chainId", chainId), + utils.LogAttr("nodeUrl", proxyUrl.Url), + utils.LogAttr("rawParsedBlock", rawData), + utils.LogAttr("verification.Value", verification.Value), + utils.LogAttr("Method", parsing.GetApiName()), + utils.LogAttr("Extension", verification.Extension), + utils.LogAttr("Addon", verification.Addon), + utils.LogAttr("Verification", verification.Name), + ) } } utils.LavaFormatInfo("[+] verified successfully", - utils.Attribute{Key: "chainId", Value: chainId}, - utils.Attribute{Key: "nodeUrl", Value: proxyUrl.Url}, - utils.Attribute{Key: "verification", Value: verification.Name}, - utils.Attribute{Key: "value", Value: parser.CapStringLen(parsedResult)}, - utils.Attribute{Key: "verificationKey", Value: verification.VerificationKey}, - utils.Attribute{Key: "apiInterface", Value: cf.endpoint.ApiInterface}, + utils.LogAttr("chainId", chainId), + utils.LogAttr("nodeUrl", proxyUrl.Url), + utils.LogAttr("verification", verification.Name), + utils.LogAttr("block", parsedInput.GetBlock()), + utils.LogAttr("rawData", parsedInput.GetRawParsedData()), + utils.LogAttr("verificationKey", verification.VerificationKey), + utils.LogAttr("apiInterface", cf.endpoint.ApiInterface), ) return nil } @@ -292,8 +295,9 @@ func (cf *ChainFetcher) FetchLatestBlockNum(ctx context.Context) (int64, error) {Key: "error", Value: err}, }...) } - blockNum, err := parser.ParseBlockFromReply(parserInput, parsing.ResultParsing) - if err != nil { + parsedInput := parser.ParseBlockFromReply(parserInput, parsing.ResultParsing, parsing.Parsers) + blockNum := parsedInput.GetBlock() + if blockNum == spectypes.NOT_APPLICABLE { return spectypes.NOT_APPLICABLE, utils.LavaFormatDebug(tagName+" Failed to parse Response", []utils.Attribute{ {Key: "chainId", Value: chainId}, {Key: "nodeUrl", Value: proxyUrl.Url}, @@ -355,7 +359,7 @@ func (cf *ChainFetcher) FetchBlockHashByNum(ctx context.Context, blockNum int64) }...) } - res, err := parser.ParseFromReplyAndDecode(parserInput, parsing.ResultParsing) + res, err := parser.ParseBlockHashFromReplyAndDecode(parserInput, parsing.ResultParsing, parsing.Parsers) if err != nil { return "", utils.LavaFormatDebug(tagName+" Failed ParseMessageResponse", []utils.Attribute{ {Key: "error", Value: err}, diff --git a/protocol/chainlib/consumer_websocket_manager.go b/protocol/chainlib/consumer_websocket_manager.go index 1d5339638e..5f47180489 100644 --- a/protocol/chainlib/consumer_websocket_manager.go +++ b/protocol/chainlib/consumer_websocket_manager.go @@ -3,9 +3,10 @@ package chainlib import ( "context" "strconv" + "sync/atomic" "time" - gojson "github.com/goccy/go-json" + "github.com/goccy/go-json" "github.com/gofiber/websocket/v2" formatter "github.com/lavanet/lava/v3/ecosystem/cache/format" "github.com/lavanet/lava/v3/protocol/common" @@ -13,6 +14,12 @@ import ( "github.com/lavanet/lava/v3/utils" "github.com/lavanet/lava/v3/utils/rand" spectypes "github.com/lavanet/lava/v3/x/spec/types" + "github.com/tidwall/gjson" +) + +var ( + WebSocketRateLimit = -1 // rate limit requests per second on websocket connection + WebSocketBanDuration = time.Duration(0) // once rate limit is reached, will not allow new incoming message for a duration ) type ConsumerWebsocketManager struct { @@ -67,7 +74,32 @@ func (cwm *ConsumerWebsocketManager) GetWebSocketConnectionUniqueId(dappId, user return dappId + "__" + userIp + "__" + cwm.WebsocketConnectionUID } +func (cwm *ConsumerWebsocketManager) handleRateLimitReached(inpData []byte) ([]byte, error) { + rateLimitError := common.JsonRpcRateLimitError + id := 0 + result := gjson.GetBytes(inpData, "id") + switch result.Type { + case gjson.Number: + id = int(result.Int()) + case gjson.String: + idParsed, err := strconv.Atoi(result.Raw) + if err == nil { + id = idParsed + } + } + rateLimitError.Id = id + bytesRateLimitError, err := json.Marshal(rateLimitError) + if err != nil { + return []byte{}, utils.LavaFormatError("failed marshalling jsonrpc rate limit error", err) + } + return bytesRateLimitError, nil +} + func (cwm *ConsumerWebsocketManager) ListenToMessages() { + // adding metrics for how many active connections we have. + cwm.rpcConsumerLogs.SetWebSocketConnectionActive(cwm.chainId, cwm.apiInterface, true) + defer cwm.rpcConsumerLogs.SetWebSocketConnectionActive(cwm.chainId, cwm.apiInterface, false) + var ( messageType int msg []byte @@ -110,6 +142,33 @@ func (cwm *ConsumerWebsocketManager) ListenToMessages() { } }() + // rate limit routine + requestsPerSecond := &atomic.Uint64{} + go func() { + if WebSocketRateLimit <= 0 { + return + } + ticker := time.NewTicker(time.Second) // rate limit per second. + defer ticker.Stop() + for { + select { + case <-webSocketCtx.Done(): + return + case <-ticker.C: + // check if rate limit reached, and ban is required + if WebSocketBanDuration > 0 && requestsPerSecond.Load() > uint64(WebSocketRateLimit) { + // wait the ban duration before resetting the store. + select { + case <-webSocketCtx.Done(): + return + case <-time.After(WebSocketBanDuration): // just continue + } + } + requestsPerSecond.Store(0) + } + } + }() + for { startTime := time.Now() msgSeed := guidString + "_" + strconv.Itoa(rand.Intn(10000000000)) // use message seed with original guid and new int @@ -125,6 +184,15 @@ func (cwm *ConsumerWebsocketManager) ListenToMessages() { break } + // Check rate limit is met + if WebSocketRateLimit > 0 && requestsPerSecond.Add(1) > uint64(WebSocketRateLimit) { + rateLimitResponse, err := cwm.handleRateLimitReached(msg) + if err == nil { + websocketConnWriteChan <- webSocketMsgWithType{messageType: messageType, msg: rateLimitResponse} + } + continue + } + dappID, ok := websocketConn.Locals("dapp-id").(string) if !ok { // Log and remove the analyze @@ -160,18 +228,17 @@ func (cwm *ConsumerWebsocketManager) ListenToMessages() { continue } - // check whether its a normal relay / unsubscribe / unsubscribe_all otherwise its a subscription flow. + // check whether it's a normal relay / unsubscribe / unsubscribe_all otherwise its a subscription flow. if !IsFunctionTagOfType(protocolMessage, spectypes.FUNCTION_TAG_SUBSCRIBE) { if IsFunctionTagOfType(protocolMessage, spectypes.FUNCTION_TAG_UNSUBSCRIBE) { err := cwm.consumerWsSubscriptionManager.Unsubscribe(webSocketCtx, protocolMessage, dappID, userIp, cwm.WebsocketConnectionUID, metricsData) if err != nil { utils.LavaFormatWarning("error unsubscribing from subscription", err, utils.LogAttr("GUID", webSocketCtx)) if err == common.SubscriptionNotFoundError { - msgData, err := gojson.Marshal(common.JsonRpcSubscriptionNotFoundError) + msgData, err := json.Marshal(common.JsonRpcSubscriptionNotFoundError) if err != nil { continue } - websocketConnWriteChan <- webSocketMsgWithType{messageType: messageType, msg: msgData} } } @@ -189,17 +256,18 @@ func (cwm *ConsumerWebsocketManager) ListenToMessages() { formatterMsg := logger.AnalyzeWebSocketErrorAndGetFormattedMessage(websocketConn.LocalAddr().String(), utils.LavaFormatError("could not send parsed relay", err), msgSeed, msg, cwm.apiInterface, time.Since(startTime)) if formatterMsg != nil { websocketConnWriteChan <- webSocketMsgWithType{messageType: messageType, msg: formatterMsg} - continue } + continue } relayResultReply := relayResult.GetReply() if relayResultReply != nil { // No need to verify signature since this is already happening inside the SendParsedRelay flow websocketConnWriteChan <- webSocketMsgWithType{messageType: messageType, msg: relayResult.GetReply().Data} - continue + } else { + utils.LavaFormatError("Relay result is nil over websocket normal request flow, should not happen", err, utils.LogAttr("messageType", messageType)) } - utils.LavaFormatError("Relay result is nil over websocket normal request flow, should not happen", err, utils.LogAttr("messageType", messageType)) + continue } } @@ -224,7 +292,7 @@ func (cwm *ConsumerWebsocketManager) ListenToMessages() { // Handle the case when the error is a method not found error if common.APINotSupportedError.Is(err) { - msgData, err := gojson.Marshal(common.JsonRpcMethodNotFoundError) + msgData, err := json.Marshal(common.JsonRpcMethodNotFoundError) if err != nil { continue } diff --git a/protocol/chainlib/consumer_ws_subscription_manager.go b/protocol/chainlib/consumer_ws_subscription_manager.go index 10d8972e38..102bd8240a 100644 --- a/protocol/chainlib/consumer_ws_subscription_manager.go +++ b/protocol/chainlib/consumer_ws_subscription_manager.go @@ -56,6 +56,7 @@ type ConsumerWSSubscriptionManager struct { activeSubscriptionProvidersStorage *lavasession.ActiveSubscriptionProvidersStorage currentlyPendingSubscriptions map[string]*pendingSubscriptionsBroadcastManager lock sync.RWMutex + consumerMetricsManager *metrics.ConsumerMetricsManager } func NewConsumerWSSubscriptionManager( @@ -65,6 +66,7 @@ func NewConsumerWSSubscriptionManager( connectionType string, chainParser ChainParser, activeSubscriptionProvidersStorage *lavasession.ActiveSubscriptionProvidersStorage, + consumerMetricsManager *metrics.ConsumerMetricsManager, ) *ConsumerWSSubscriptionManager { return &ConsumerWSSubscriptionManager{ connectedDapps: make(map[string]map[string]*common.SafeChannelSender[*pairingtypes.RelayReply]), @@ -76,6 +78,7 @@ func NewConsumerWSSubscriptionManager( relaySender: relaySender, connectionType: connectionType, activeSubscriptionProvidersStorage: activeSubscriptionProvidersStorage, + consumerMetricsManager: consumerMetricsManager, } } @@ -216,6 +219,7 @@ func (cwsm *ConsumerWSSubscriptionManager) StartSubscription( // called after send relay failure or parsing failure afterwards onSubscriptionFailure := func() { + go cwsm.consumerMetricsManager.SetFailedWsSubscriptionRequestMetric(metricsData.ChainID, metricsData.APIType) cwsm.failedPendingSubscription(hashedParams) closeWebsocketRepliesChannel() } @@ -255,6 +259,7 @@ func (cwsm *ConsumerWSSubscriptionManager) StartSubscription( // Validated there are no active subscriptions that we can use. firstSubscriptionReply, returnWebsocketRepliesChan := cwsm.checkForActiveSubscriptionWithLock(webSocketCtx, hashedParams, protocolMessage, dappKey, websocketRepliesSafeChannelSender, closeWebsocketRepliesChannel) if firstSubscriptionReply != nil { + go cwsm.consumerMetricsManager.SetDuplicatedWsSubscriptionRequestMetric(metricsData.ChainID, metricsData.APIType) if returnWebsocketRepliesChan { return firstSubscriptionReply, websocketRepliesChan, nil } @@ -412,7 +417,7 @@ func (cwsm *ConsumerWSSubscriptionManager) StartSubscription( cwsm.successfulPendingSubscription(hashedParams) // Need to be run once for subscription go cwsm.listenForSubscriptionMessages(webSocketCtx, dappID, consumerIp, replyServer, hashedParams, providerAddr, metricsData, closeSubscriptionChan) - + go cwsm.consumerMetricsManager.SetWsSubscriptionRequestMetric(metricsData.ChainID, metricsData.APIType) return &reply, websocketRepliesChan, nil } @@ -524,12 +529,14 @@ func (cwsm *ConsumerWSSubscriptionManager) listenForSubscriptionMessages( utils.LogAttr("GUID", webSocketCtx), utils.LogAttr("hashedParams", utils.ToHexString(hashedParams)), ) + go cwsm.consumerMetricsManager.SetWsSubscriptioDisconnectRequestMetric(metricsData.ChainID, metricsData.APIType, metrics.WsDisconnectionReasonUser) return case <-replyServer.Context().Done(): utils.LavaFormatTrace("reply server context canceled", utils.LogAttr("GUID", webSocketCtx), utils.LogAttr("hashedParams", utils.ToHexString(hashedParams)), ) + go cwsm.consumerMetricsManager.SetWsSubscriptioDisconnectRequestMetric(metricsData.ChainID, metricsData.APIType, metrics.WsDisconnectionReasonConsumer) return default: var reply pairingtypes.RelayReply @@ -537,6 +544,7 @@ func (cwsm *ConsumerWSSubscriptionManager) listenForSubscriptionMessages( if err != nil { // The connection was closed by the provider utils.LavaFormatTrace("error reading from subscription stream", utils.LogAttr("original error", err.Error())) + go cwsm.consumerMetricsManager.SetWsSubscriptioDisconnectRequestMetric(metricsData.ChainID, metricsData.APIType, metrics.WsDisconnectionReasonProvider) return } err = cwsm.handleIncomingSubscriptionNodeMessage(hashedParams, &reply, providerAddr) @@ -545,6 +553,7 @@ func (cwsm *ConsumerWSSubscriptionManager) listenForSubscriptionMessages( utils.LogAttr("hashedParams", hashedParams), utils.LogAttr("reply", reply), ) + go cwsm.consumerMetricsManager.SetFailedWsSubscriptionRequestMetric(metricsData.ChainID, metricsData.APIType) return } } diff --git a/protocol/chainlib/consumer_ws_subscription_manager_test.go b/protocol/chainlib/consumer_ws_subscription_manager_test.go index c549cb6772..48573a3512 100644 --- a/protocol/chainlib/consumer_ws_subscription_manager_test.go +++ b/protocol/chainlib/consumer_ws_subscription_manager_test.go @@ -2,9 +2,11 @@ package chainlib import ( "context" + "fmt" "strconv" "strings" "sync" + "sync/atomic" "testing" "time" @@ -27,6 +29,9 @@ import ( const ( numberOfParallelSubscriptions = 10 uniqueId = "1234" + projectHashTest = "test_projecthash" + chainIdTest = "test_chainId" + apiTypeTest = "test_apiType" ) func TestConsumerWSSubscriptionManagerParallelSubscriptionsOnSameDappIdIp(t *testing.T) { @@ -51,7 +56,7 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptionsOnSameDappIdIp(t *tes subscriptionFirstReply2: []byte(`{"jsonrpc":"2.0","id":4,"result":{}}`), }, } - + metricsData := metrics.NewRelayAnalytics(projectHashTest, chainIdTest, apiTypeTest) for _, play := range playbook { t.Run(play.name, func(t *testing.T) { ts := SetupForTests(t, 1, play.specId, "../../") @@ -136,7 +141,7 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptionsOnSameDappIdIp(t *tes consumerSessionManager := CreateConsumerSessionManager(play.specId, play.apiInterface, ts.Consumer.Addr.String()) // Create a new ConsumerWSSubscriptionManager - manager := NewConsumerWSSubscriptionManager(consumerSessionManager, relaySender, nil, play.connectionType, chainParser, lavasession.NewActiveSubscriptionProvidersStorage()) + manager := NewConsumerWSSubscriptionManager(consumerSessionManager, relaySender, nil, play.connectionType, chainParser, lavasession.NewActiveSubscriptionProvidersStorage(), nil) uniqueIdentifiers := make([]string, numberOfParallelSubscriptions) wg := sync.WaitGroup{} wg.Add(numberOfParallelSubscriptions) @@ -151,7 +156,7 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptionsOnSameDappIdIp(t *tes var repliesChan <-chan *pairingtypes.RelayReply var firstReply *pairingtypes.RelayReply - firstReply, repliesChan, err = manager.StartSubscription(ctx, protocolMessage1, dapp, ip, uniqueIdentifiers[index], nil) + firstReply, repliesChan, err = manager.StartSubscription(ctx, protocolMessage1, dapp, ip, uniqueIdentifiers[index], metricsData) go func() { for subMsg := range repliesChan { // utils.LavaFormatInfo("got reply for index", utils.LogAttr("index", index)) @@ -169,7 +174,7 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptionsOnSameDappIdIp(t *tes // now we have numberOfParallelSubscriptions subscriptions currently running require.Len(t, manager.connectedDapps, numberOfParallelSubscriptions) // remove one - err = manager.Unsubscribe(ts.Ctx, protocolMessage1, dapp, ip, uniqueIdentifiers[0], nil) + err = manager.Unsubscribe(ts.Ctx, protocolMessage1, dapp, ip, uniqueIdentifiers[0], metricsData) require.NoError(t, err) // now we have numberOfParallelSubscriptions - 1 require.Len(t, manager.connectedDapps, numberOfParallelSubscriptions-1) @@ -177,7 +182,7 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptionsOnSameDappIdIp(t *tes require.Len(t, manager.activeSubscriptions, 1) // same flow for unsubscribe all - err = manager.UnsubscribeAll(ts.Ctx, dapp, ip, uniqueIdentifiers[1], nil) + err = manager.UnsubscribeAll(ts.Ctx, dapp, ip, uniqueIdentifiers[1], metricsData) require.NoError(t, err) // now we have numberOfParallelSubscriptions - 2 require.Len(t, manager.connectedDapps, numberOfParallelSubscriptions-2) @@ -209,7 +214,6 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptions(t *testing.T) { subscriptionFirstReply2: []byte(`{"jsonrpc":"2.0","id":4,"result":{}}`), }, } - for _, play := range playbook { t.Run(play.name, func(t *testing.T) { ts := SetupForTests(t, 1, play.specId, "../../") @@ -291,9 +295,9 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptions(t *testing.T) { Times(1) // Should call SendParsedRelay, because it is the first time we subscribe consumerSessionManager := CreateConsumerSessionManager(play.specId, play.apiInterface, ts.Consumer.Addr.String()) - + metricsData := metrics.NewRelayAnalytics(projectHashTest, chainIdTest, apiTypeTest) // Create a new ConsumerWSSubscriptionManager - manager := NewConsumerWSSubscriptionManager(consumerSessionManager, relaySender, nil, play.connectionType, chainParser, lavasession.NewActiveSubscriptionProvidersStorage()) + manager := NewConsumerWSSubscriptionManager(consumerSessionManager, relaySender, nil, play.connectionType, chainParser, lavasession.NewActiveSubscriptionProvidersStorage(), nil) wg := sync.WaitGroup{} wg.Add(10) @@ -305,7 +309,7 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptions(t *testing.T) { ctx := utils.WithUniqueIdentifier(ts.Ctx, utils.GenerateUniqueIdentifier()) var repliesChan <-chan *pairingtypes.RelayReply var firstReply *pairingtypes.RelayReply - firstReply, repliesChan, err = manager.StartSubscription(ctx, protocolMessage1, dapp+strconv.Itoa(index), ts.Consumer.Addr.String(), uniqueId, nil) + firstReply, repliesChan, err = manager.StartSubscription(ctx, protocolMessage1, dapp+strconv.Itoa(index), ts.Consumer.Addr.String(), uniqueId, metricsData) go func() { for subMsg := range repliesChan { require.Equal(t, string(play.subscriptionFirstReply1), string(subMsg.Data)) @@ -322,6 +326,11 @@ func TestConsumerWSSubscriptionManagerParallelSubscriptions(t *testing.T) { } } +func TestRateLimit(t *testing.T) { + numberOfRequests := &atomic.Uint64{} + fmt.Println(numberOfRequests.Load()) +} + func TestConsumerWSSubscriptionManager(t *testing.T) { // This test does the following: // 1. Create a new ConsumerWSSubscriptionManager @@ -379,6 +388,7 @@ func TestConsumerWSSubscriptionManager(t *testing.T) { unsubscribeMessage2: []byte(`{"jsonrpc":"2.0","method":"eth_unsubscribe","params":["0x2134567890"],"id":1}`), }, } + metricsData := metrics.NewRelayAnalytics(projectHashTest, chainIdTest, apiTypeTest) for _, play := range playbook { t.Run(play.name, func(t *testing.T) { @@ -538,12 +548,12 @@ func TestConsumerWSSubscriptionManager(t *testing.T) { consumerSessionManager := CreateConsumerSessionManager(play.specId, play.apiInterface, ts.Consumer.Addr.String()) // Create a new ConsumerWSSubscriptionManager - manager := NewConsumerWSSubscriptionManager(consumerSessionManager, relaySender, nil, play.connectionType, chainParser, lavasession.NewActiveSubscriptionProvidersStorage()) + manager := NewConsumerWSSubscriptionManager(consumerSessionManager, relaySender, nil, play.connectionType, chainParser, lavasession.NewActiveSubscriptionProvidersStorage(), nil) // Start a new subscription for the first time, called SendParsedRelay once ctx = utils.WithUniqueIdentifier(ctx, utils.GenerateUniqueIdentifier()) - firstReply, repliesChan1, err := manager.StartSubscription(ctx, subscribeProtocolMessage1, dapp1, ts.Consumer.Addr.String(), uniqueId, nil) + firstReply, repliesChan1, err := manager.StartSubscription(ctx, subscribeProtocolMessage1, dapp1, ts.Consumer.Addr.String(), uniqueId, metricsData) assert.NoError(t, err) unsubscribeMessageWg.Add(1) assert.Equal(t, string(play.subscriptionFirstReply1), string(firstReply.Data)) @@ -559,7 +569,7 @@ func TestConsumerWSSubscriptionManager(t *testing.T) { // Start a subscription again, same params, same dappKey, should not call SendParsedRelay ctx = utils.WithUniqueIdentifier(ctx, utils.GenerateUniqueIdentifier()) - firstReply, repliesChan2, err := manager.StartSubscription(ctx, subscribeProtocolMessage1, dapp1, ts.Consumer.Addr.String(), uniqueId, nil) + firstReply, repliesChan2, err := manager.StartSubscription(ctx, subscribeProtocolMessage1, dapp1, ts.Consumer.Addr.String(), uniqueId, metricsData) assert.NoError(t, err) assert.Equal(t, string(play.subscriptionFirstReply1), string(firstReply.Data)) assert.Nil(t, repliesChan2) // Same subscription, same dappKey, no need for a new channel @@ -568,7 +578,7 @@ func TestConsumerWSSubscriptionManager(t *testing.T) { // Start a subscription again, same params, different dappKey, should not call SendParsedRelay ctx = utils.WithUniqueIdentifier(ctx, utils.GenerateUniqueIdentifier()) - firstReply, repliesChan3, err := manager.StartSubscription(ctx, subscribeProtocolMessage1, dapp2, ts.Consumer.Addr.String(), uniqueId, nil) + firstReply, repliesChan3, err := manager.StartSubscription(ctx, subscribeProtocolMessage1, dapp2, ts.Consumer.Addr.String(), uniqueId, metricsData) assert.NoError(t, err) assert.Equal(t, string(play.subscriptionFirstReply1), string(firstReply.Data)) assert.NotNil(t, repliesChan3) // Same subscription, but different dappKey, so will create new channel @@ -652,7 +662,7 @@ func TestConsumerWSSubscriptionManager(t *testing.T) { // Start a subscription again, different params, same dappKey, should call SendParsedRelay ctx = utils.WithUniqueIdentifier(ctx, utils.GenerateUniqueIdentifier()) - firstReply, repliesChan4, err := manager.StartSubscription(ctx, subscribeProtocolMessage2, dapp1, ts.Consumer.Addr.String(), uniqueId, nil) + firstReply, repliesChan4, err := manager.StartSubscription(ctx, subscribeProtocolMessage2, dapp1, ts.Consumer.Addr.String(), uniqueId, metricsData) assert.NoError(t, err) unsubscribeMessageWg.Add(1) assert.Equal(t, string(play.subscriptionFirstReply2), string(firstReply.Data)) @@ -671,7 +681,7 @@ func TestConsumerWSSubscriptionManager(t *testing.T) { ctx = utils.WithUniqueIdentifier(ctx, utils.GenerateUniqueIdentifier()) unsubProtocolMessage := NewProtocolMessage(unsubscribeChainMessage1, nil, relayResult1.Request.RelayData, dapp2, ts.Consumer.Addr.String()) - err = manager.Unsubscribe(ctx, unsubProtocolMessage, dapp2, ts.Consumer.Addr.String(), uniqueId, nil) + err = manager.Unsubscribe(ctx, unsubProtocolMessage, dapp2, ts.Consumer.Addr.String(), uniqueId, metricsData) require.NoError(t, err) listenForExpectedMessages(ctx, repliesChan1, string(play.subscriptionFirstReply1)) @@ -697,7 +707,7 @@ func TestConsumerWSSubscriptionManager(t *testing.T) { Times(2) // Should call SendParsedRelay, because it unsubscribed ctx = utils.WithUniqueIdentifier(ctx, utils.GenerateUniqueIdentifier()) - err = manager.UnsubscribeAll(ctx, dapp1, ts.Consumer.Addr.String(), uniqueId, nil) + err = manager.UnsubscribeAll(ctx, dapp1, ts.Consumer.Addr.String(), uniqueId, metricsData) require.NoError(t, err) expectNoMoreMessages(ctx, repliesChan1) diff --git a/protocol/chainlib/grpc_test.go b/protocol/chainlib/grpc_test.go index 8c51ab9ca1..68e5d2ece5 100644 --- a/protocol/chainlib/grpc_test.go +++ b/protocol/chainlib/grpc_test.go @@ -217,9 +217,8 @@ func TestParsingRequestedBlocksHeadersGrpc(t *testing.T) { require.NoError(t, err) parserInput, err := FormatResponseForParsing(reply.RelayReply, chainMessage) require.NoError(t, err) - blockNum, err := parser.ParseBlockFromReply(parserInput, parsingForCrafting.ResultParsing) - require.NoError(t, err) - require.Equal(t, test.block, blockNum) + parsedInput := parser.ParseBlockFromReply(parserInput, parsingForCrafting.ResultParsing, nil) + require.Equal(t, test.block, parsedInput.GetBlock()) }) } } @@ -287,9 +286,8 @@ func TestSettingBlocksHeadersGrpc(t *testing.T) { require.NoError(t, err) parserInput, err := FormatResponseForParsing(reply.RelayReply, chainMessage) require.NoError(t, err) - blockNum, err := parser.ParseBlockFromReply(parserInput, parsingForCrafting.ResultParsing) - require.NoError(t, err) - require.Equal(t, test.block, blockNum) + parsedInput := parser.ParseBlockFromReply(parserInput, parsingForCrafting.ResultParsing, nil) + require.Equal(t, test.block, parsedInput.GetBlock()) }) } } diff --git a/protocol/chainlib/grpcproxy/grpcproxy.go b/protocol/chainlib/grpcproxy/grpcproxy.go index 7ec2ec0d2c..d9e86fa48b 100644 --- a/protocol/chainlib/grpcproxy/grpcproxy.go +++ b/protocol/chainlib/grpcproxy/grpcproxy.go @@ -112,7 +112,7 @@ func (RawBytesCodec) Marshal(v interface{}) ([]byte, error) { func (RawBytesCodec) Unmarshal(data []byte, v interface{}) error { bufferPtr, ok := v.(*[]byte) if !ok { - return utils.LavaFormatError("cannot decode into type", nil, utils.Attribute{Key: "v", Value: v}) + return utils.LavaFormatDebug("cannot decode into type", utils.LogAttr("v", v), utils.LogAttr("data", data)) } *bufferPtr = data return nil diff --git a/protocol/chainlib/jsonRPC_test.go b/protocol/chainlib/jsonRPC_test.go index f32ec51683..7158e98704 100644 --- a/protocol/chainlib/jsonRPC_test.go +++ b/protocol/chainlib/jsonRPC_test.go @@ -187,8 +187,9 @@ func TestJsonRpcChainProxy(t *testing.T) { require.NoError(t, err) _, err = chainFetcher.FetchBlockHashByNum(ctx, block) - errMsg := "GET_BLOCK_BY_NUM Failed ParseMessageResponse {error:invalid parser input format" - require.True(t, err.Error()[:len(errMsg)] == errMsg, err.Error()) + actualErrMsg := "GET_BLOCK_BY_NUM Failed ParseMessageResponse {error:blockParsing - parse failed {error:invalid parser input format," + expectedErrMsg := err.Error()[:len(actualErrMsg)] + require.Equal(t, actualErrMsg, expectedErrMsg, err.Error()) } func TestAddonAndVerifications(t *testing.T) { diff --git a/protocol/chainlib/rest_test.go b/protocol/chainlib/rest_test.go index 6ce0922113..918888bf1a 100644 --- a/protocol/chainlib/rest_test.go +++ b/protocol/chainlib/rest_test.go @@ -214,9 +214,8 @@ func TestParsingRequestedBlocksHeadersRest(t *testing.T) { require.NoError(t, err) parserInput, err := FormatResponseForParsing(reply.RelayReply, chainMessage) require.NoError(t, err) - blockNum, err := parser.ParseBlockFromReply(parserInput, parsingForCrafting.ResultParsing) - require.NoError(t, err) - require.Equal(t, test.block, blockNum) + parsedInput := parser.ParseBlockFromReply(parserInput, parsingForCrafting.ResultParsing, nil) + require.Equal(t, test.block, parsedInput.GetBlock()) }) } } @@ -289,9 +288,8 @@ func TestSettingRequestedBlocksHeadersRest(t *testing.T) { require.NoError(t, err) parserInput, err := FormatResponseForParsing(reply.RelayReply, chainMessage) require.NoError(t, err) - blockNum, err := parser.ParseBlockFromReply(parserInput, parsingForCrafting.ResultParsing) - require.NoError(t, err) - require.Equal(t, test.block, blockNum) + parsedInput := parser.ParseBlockFromReply(parserInput, parsingForCrafting.ResultParsing, nil) + require.Equal(t, test.block, parsedInput.GetBlock()) }) } } diff --git a/protocol/common/cobra_common.go b/protocol/common/cobra_common.go index 40cbffdce2..7574e9dd23 100644 --- a/protocol/common/cobra_common.go +++ b/protocol/common/cobra_common.go @@ -38,6 +38,10 @@ const ( SetProviderOptimizerBestTierPickChance = "set-provider-optimizer-best-tier-pick-chance" SetProviderOptimizerWorstTierPickChance = "set-provider-optimizer-worst-tier-pick-chance" SetProviderOptimizerNumberOfTiersToCreate = "set-provider-optimizer-number-of-tiers-to-create" + + // websocket flags + RateLimitWebSocketFlag = "rate-limit-websocket-requests-per-connection" + BanDurationForWebsocketRateLimitExceededFlag = "ban-duration-for-websocket-rate-limit-exceeded" ) const ( diff --git a/protocol/common/endpoints.go b/protocol/common/endpoints.go index 99cd6504ac..435fcde26c 100644 --- a/protocol/common/endpoints.go +++ b/protocol/common/endpoints.go @@ -28,6 +28,7 @@ const ( NODE_ERRORS_PROVIDERS_HEADER_NAME = "Lava-Node-Errors-providers" REPORTED_PROVIDERS_HEADER_NAME = "Lava-Reported-Providers" USER_REQUEST_TYPE = "lava-user-request-type" + STATEFUL_API_HEADER = "lava-stateful-api" LAVA_IDENTIFIED_NODE_ERROR_HEADER = "lava-identified-node-error" LAVAP_VERSION_HEADER_NAME = "Lavap-Version" LAVA_CONSUMER_PROCESS_GUID = "lava-consumer-process-guid" diff --git a/protocol/common/return_errors.go b/protocol/common/return_errors.go index 5394ba1f3d..9020a26f17 100644 --- a/protocol/common/return_errors.go +++ b/protocol/common/return_errors.go @@ -27,6 +27,15 @@ var JsonRpcMethodNotFoundError = JsonRPCErrorMessage{ }, } +var JsonRpcRateLimitError = JsonRPCErrorMessage{ + JsonRPC: "2.0", + Id: 1, + Error: JsonRPCError{ + Code: 429, + Message: "Too Many Requests", + }, +} + var JsonRpcSubscriptionNotFoundError = JsonRPCErrorMessage{ JsonRPC: "2.0", Id: 1, diff --git a/protocol/integration/protocol_test.go b/protocol/integration/protocol_test.go index 048bafedda..cf3a0412d0 100644 --- a/protocol/integration/protocol_test.go +++ b/protocol/integration/protocol_test.go @@ -10,11 +10,13 @@ import ( "net/url" "os" "strconv" + "strings" "sync" "testing" "time" "github.com/gorilla/websocket" + "github.com/lavanet/lava/v3/ecosystem/cache" "github.com/lavanet/lava/v3/protocol/chainlib" "github.com/lavanet/lava/v3/protocol/chainlib/chainproxy/rpcInterfaceMessages" "github.com/lavanet/lava/v3/protocol/chaintracker" @@ -22,6 +24,7 @@ import ( "github.com/lavanet/lava/v3/protocol/lavaprotocol/finalizationconsensus" "github.com/lavanet/lava/v3/protocol/lavasession" "github.com/lavanet/lava/v3/protocol/metrics" + "github.com/lavanet/lava/v3/protocol/performance" "github.com/lavanet/lava/v3/protocol/provideroptimizer" "github.com/lavanet/lava/v3/protocol/rpcconsumer" "github.com/lavanet/lava/v3/protocol/rpcprovider" @@ -162,21 +165,38 @@ func createInMemoryRewardDb(specs []string) (*rewardserver.RewardDB, error) { return rewardDB, nil } -func createRpcConsumer(t *testing.T, ctx context.Context, specId string, apiInterface string, account sigs.Account, consumerListenAddress string, epoch uint64, pairingList map[uint64]*lavasession.ConsumerSessionsWithProvider, requiredResponses int, lavaChainID string) (*rpcconsumer.RPCConsumerServer, *mockConsumerStateTracker) { +type rpcConsumerOptions struct { + specId string + apiInterface string + account sigs.Account + consumerListenAddress string + epoch uint64 + pairingList map[uint64]*lavasession.ConsumerSessionsWithProvider + requiredResponses int + lavaChainID string + cacheListenAddress string +} + +type rpcConsumerOut struct { + rpcConsumerServer *rpcconsumer.RPCConsumerServer + mockConsumerStateTracker *mockConsumerStateTracker +} + +func createRpcConsumer(t *testing.T, ctx context.Context, rpcConsumerOptions rpcConsumerOptions) rpcConsumerOut { serverHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Handle the incoming request and provide the desired response w.WriteHeader(http.StatusOK) }) - chainParser, _, chainFetcher, _, _, err := chainlib.CreateChainLibMocks(ctx, specId, apiInterface, serverHandler, nil, "../../", nil) + chainParser, _, chainFetcher, _, _, err := chainlib.CreateChainLibMocks(ctx, rpcConsumerOptions.specId, rpcConsumerOptions.apiInterface, serverHandler, nil, "../../", nil) require.NoError(t, err) require.NotNil(t, chainParser) require.NotNil(t, chainFetcher) rpcConsumerServer := &rpcconsumer.RPCConsumerServer{} rpcEndpoint := &lavasession.RPCEndpoint{ - NetworkAddress: consumerListenAddress, - ChainID: specId, - ApiInterface: apiInterface, + NetworkAddress: rpcConsumerOptions.consumerListenAddress, + ChainID: rpcConsumerOptions.specId, + ApiInterface: rpcConsumerOptions.apiInterface, TLSEnabled: false, HealthCheckPath: "", Geolocation: 1, @@ -187,13 +207,21 @@ func createRpcConsumer(t *testing.T, ctx context.Context, specId string, apiInte baseLatency := common.AverageWorldLatency / 2 optimizer := provideroptimizer.NewProviderOptimizer(provideroptimizer.STRATEGY_BALANCED, averageBlockTime, baseLatency, 2) consumerSessionManager := lavasession.NewConsumerSessionManager(rpcEndpoint, optimizer, nil, nil, "test", lavasession.NewActiveSubscriptionProvidersStorage()) - consumerSessionManager.UpdateAllProviders(epoch, pairingList) + consumerSessionManager.UpdateAllProviders(rpcConsumerOptions.epoch, rpcConsumerOptions.pairingList) + + var cache *performance.Cache = nil + if rpcConsumerOptions.cacheListenAddress != "" { + cache, err = performance.InitCache(ctx, rpcConsumerOptions.cacheListenAddress) + if err != nil { + t.Fatalf("Failed To Connect to cache at address %s: %v", rpcConsumerOptions.cacheListenAddress, err) + } + } - consumerConsistency := rpcconsumer.NewConsumerConsistency(specId) + consumerConsistency := rpcconsumer.NewConsumerConsistency(rpcConsumerOptions.specId) consumerCmdFlags := common.ConsumerCmdFlags{} rpcconsumerLogs, err := metrics.NewRPCConsumerLogs(nil, nil) require.NoError(t, err) - err = rpcConsumerServer.ServeRPCRequests(ctx, rpcEndpoint, consumerStateTracker, chainParser, finalizationConsensus, consumerSessionManager, requiredResponses, account.SK, lavaChainID, nil, rpcconsumerLogs, account.Addr, consumerConsistency, nil, consumerCmdFlags, false, nil, nil, nil) + err = rpcConsumerServer.ServeRPCRequests(ctx, rpcEndpoint, consumerStateTracker, chainParser, finalizationConsensus, consumerSessionManager, rpcConsumerOptions.requiredResponses, rpcConsumerOptions.account.SK, rpcConsumerOptions.lavaChainID, cache, rpcconsumerLogs, rpcConsumerOptions.account.Addr, consumerConsistency, nil, consumerCmdFlags, false, nil, nil, nil) require.NoError(t, err) // wait for consumer to finish initialization @@ -211,10 +239,22 @@ func createRpcConsumer(t *testing.T, ctx context.Context, specId string, apiInte require.True(t, consumerUp) } - return rpcConsumerServer, consumerStateTracker + return rpcConsumerOut{rpcConsumerServer, consumerStateTracker} +} + +type rpcProviderOptions struct { + consumerAddress string + specId string + apiInterface string + listenAddress string + account sigs.Account + lavaChainID string + addons []string + providerUniqueId string + cacheListenAddress string } -func createRpcProvider(t *testing.T, ctx context.Context, consumerAddress string, specId string, apiInterface string, listenAddress string, account sigs.Account, lavaChainID string, addons []string, providerUniqueId string) (*rpcprovider.RPCProviderServer, *lavasession.RPCProviderEndpoint, *ReplySetter, *MockChainFetcher, *MockReliabilityManager) { +func createRpcProvider(t *testing.T, ctx context.Context, rpcProviderOptions rpcProviderOptions) (*rpcprovider.RPCProviderServer, *lavasession.RPCProviderEndpoint, *ReplySetter, *MockChainFetcher, *MockReliabilityManager) { replySetter := ReplySetter{ status: http.StatusOK, replyDataBuf: []byte(`{"reply": "REPLY-STUB"}`), @@ -234,16 +274,16 @@ func createRpcProvider(t *testing.T, ctx context.Context, consumerAddress string fmt.Fprint(w, string(data)) }) - chainParser, chainRouter, chainFetcher, _, endpoint, err := chainlib.CreateChainLibMocks(ctx, specId, apiInterface, serverHandler, nil, "../../", addons) + chainParser, chainRouter, chainFetcher, _, endpoint, err := chainlib.CreateChainLibMocks(ctx, rpcProviderOptions.specId, rpcProviderOptions.apiInterface, serverHandler, nil, "../../", rpcProviderOptions.addons) require.NoError(t, err) require.NotNil(t, chainParser) require.NotNil(t, chainFetcher) require.NotNil(t, chainRouter) - endpoint.NetworkAddress.Address = listenAddress + endpoint.NetworkAddress.Address = rpcProviderOptions.listenAddress rpcProviderServer := &rpcprovider.RPCProviderServer{} - if providerUniqueId != "" { - rpcProviderServer.SetProviderUniqueId(providerUniqueId) + if rpcProviderOptions.providerUniqueId != "" { + rpcProviderServer.SetProviderUniqueId(rpcProviderOptions.providerUniqueId) } rpcProviderEndpoint := &lavasession.RPCProviderEndpoint{ @@ -253,8 +293,8 @@ func createRpcProvider(t *testing.T, ctx context.Context, consumerAddress string CertPem: "", DisableTLS: false, }, - ChainID: specId, - ApiInterface: apiInterface, + ChainID: rpcProviderOptions.specId, + ApiInterface: rpcProviderOptions.apiInterface, Geolocation: 1, NodeUrls: []common.NodeUrl{ { @@ -263,22 +303,22 @@ func createRpcProvider(t *testing.T, ctx context.Context, consumerAddress string AuthConfig: common.AuthConfig{}, IpForwarding: false, Timeout: 0, - Addons: addons, + Addons: rpcProviderOptions.addons, SkipVerifications: []string{}, }, }, } - rewardDB, err := createInMemoryRewardDb([]string{specId}) + rewardDB, err := createInMemoryRewardDb([]string{rpcProviderOptions.specId}) require.NoError(t, err) _, averageBlockTime, blocksToFinalization, blocksInFinalizationData := chainParser.ChainBlockStats() - mockProviderStateTracker := mockProviderStateTracker{consumerAddressForPairing: consumerAddress, averageBlockTime: averageBlockTime} + mockProviderStateTracker := mockProviderStateTracker{consumerAddressForPairing: rpcProviderOptions.consumerAddress, averageBlockTime: averageBlockTime} rws := rewardserver.NewRewardServer(&mockProviderStateTracker, nil, rewardDB, "badger_test", 1, 10, nil) blockMemorySize, err := mockProviderStateTracker.GetEpochSizeMultipliedByRecommendedEpochNumToCollectPayment(ctx) require.NoError(t, err) providerSessionManager := lavasession.NewProviderSessionManager(rpcProviderEndpoint, blockMemorySize) providerPolicy := rpcprovider.GetAllAddonsAndExtensionsFromNodeUrlSlice(rpcProviderEndpoint.NodeUrls) - chainParser.SetPolicy(providerPolicy, specId, apiInterface) + chainParser.SetPolicy(providerPolicy, rpcProviderOptions.specId, rpcProviderOptions.apiInterface) blocksToSaveChainTracker := uint64(blocksToFinalization + blocksInFinalizationData) chainTrackerConfig := chaintracker.ChainTrackerConfig{ @@ -290,13 +330,21 @@ func createRpcProvider(t *testing.T, ctx context.Context, consumerAddress string Pmetrics: nil, } + var cache *performance.Cache = nil + if rpcProviderOptions.cacheListenAddress != "" { + cache, err = performance.InitCache(ctx, rpcProviderOptions.cacheListenAddress) + if err != nil { + t.Fatalf("Failed To Connect to cache at address %s: %v", rpcProviderOptions.cacheListenAddress, err) + } + } + mockChainFetcher := NewMockChainFetcher(1000, int64(blocksToSaveChainTracker), nil) chainTracker, err := chaintracker.NewChainTracker(ctx, mockChainFetcher, chainTrackerConfig) require.NoError(t, err) chainTracker.StartAndServe(ctx) - reliabilityManager := reliabilitymanager.NewReliabilityManager(chainTracker, &mockProviderStateTracker, account.Addr.String(), chainRouter, chainParser) + reliabilityManager := reliabilitymanager.NewReliabilityManager(chainTracker, &mockProviderStateTracker, rpcProviderOptions.account.Addr.String(), chainRouter, chainParser) mockReliabilityManager := NewMockReliabilityManager(reliabilityManager) - rpcProviderServer.ServeRPCRequests(ctx, rpcProviderEndpoint, chainParser, rws, providerSessionManager, mockReliabilityManager, account.SK, nil, chainRouter, &mockProviderStateTracker, account.Addr, lavaChainID, rpcprovider.DEFAULT_ALLOWED_MISSING_CU, nil, nil, nil, false) + rpcProviderServer.ServeRPCRequests(ctx, rpcProviderEndpoint, chainParser, rws, providerSessionManager, mockReliabilityManager, rpcProviderOptions.account.SK, cache, chainRouter, &mockProviderStateTracker, rpcProviderOptions.account.Addr, rpcProviderOptions.lavaChainID, rpcprovider.DEFAULT_ALLOWED_MISSING_CU, nil, nil, nil, false) listener := rpcprovider.NewProviderListener(ctx, rpcProviderEndpoint.NetworkAddress, "/health") err = listener.RegisterReceiver(rpcProviderServer, rpcProviderEndpoint) require.NoError(t, err) @@ -307,13 +355,31 @@ func createRpcProvider(t *testing.T, ctx context.Context, consumerAddress string return rpcProviderServer, endpoint, &replySetter, mockChainFetcher, mockReliabilityManager } +func createCacheServer(t *testing.T, ctx context.Context, listenAddress string) { + go func() { + cs := cache.CacheServer{CacheMaxCost: 2 * 1024 * 1024 * 1024} // taken from max-items default value + cs.InitCache( + ctx, + cache.DefaultExpirationTimeFinalized, + cache.DefaultExpirationForNonFinalized, + cache.DefaultExpirationNodeErrors, + cache.DefaultExpirationBlocksHashesToHeights, + "disabled", + cache.DefaultExpirationTimeFinalizedMultiplier, + cache.DefaultExpirationTimeNonFinalizedMultiplier, + ) + cs.Serve(ctx, listenAddress) + }() + cacheServerUp := checkServerStatusWithTimeout("http://"+listenAddress, time.Second*7) + require.True(t, cacheServerUp) +} + func TestConsumerProviderBasic(t *testing.T) { ctx := context.Background() // can be any spec and api interface specId := "LAV1" - apiInterface := spectypes.APIInterfaceTendermintRPC + apiInterface := spectypes.APIInterfaceRest epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := 1 @@ -339,7 +405,17 @@ func TestConsumerProviderBasic(t *testing.T) { ctx := context.Background() providerDataI := providers[i] listenAddress := addressGen.GetAddress() - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, []string(nil), fmt.Sprintf("provider%d", i)) + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, rpcProviderOptions) } for i := 0; i < numProviders; i++ { pairingList[uint64(i)] = &lavasession.ConsumerSessionsWithProvider{ @@ -357,10 +433,21 @@ func TestConsumerProviderBasic(t *testing.T) { PairingEpoch: epoch, } } - rpcconsumerServer, _ := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) - require.NotNil(t, rpcconsumerServer) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) client := http.Client{} - resp, err := client.Get("http://" + consumerListenAddress + "/status") + resp, err := client.Get("http://" + consumerListenAddress + "/cosmos/base/tendermint/v1beta1/blocks/latest") require.NoError(t, err) require.Equal(t, http.StatusOK, resp.StatusCode) bodyBytes, err := io.ReadAll(resp.Body) @@ -390,7 +477,6 @@ func TestConsumerProviderWithProviders(t *testing.T) { specId := "LAV1" apiInterface := spectypes.APIInterfaceTendermintRPC epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := 5 @@ -415,7 +501,17 @@ func TestConsumerProviderWithProviders(t *testing.T) { ctx := context.Background() providerDataI := providers[i] listenAddress := addressGen.GetAddress() - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, []string(nil), fmt.Sprintf("provider%d", i)) + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, rpcProviderOptions) providers[i].replySetter.replyDataBuf = []byte(fmt.Sprintf(`{"reply": %d}`, i+1)) } for i := 0; i < numProviders; i++ { @@ -434,8 +530,19 @@ func TestConsumerProviderWithProviders(t *testing.T) { PairingEpoch: epoch, } } - rpcconsumerServer, _ := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) - require.NotNil(t, rpcconsumerServer) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) if play.scenario != 1 { counter := map[int]int{} for i := 0; i <= 1000; i++ { @@ -524,7 +631,6 @@ func TestConsumerProviderTx(t *testing.T) { specId := "LAV1" apiInterface := spectypes.APIInterfaceRest epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := 5 @@ -550,7 +656,17 @@ func TestConsumerProviderTx(t *testing.T) { ctx := context.Background() providerDataI := providers[i] listenAddress := addressGen.GetAddress() - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, []string(nil), fmt.Sprintf("provider%d", i)) + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, rpcProviderOptions) providers[i].replySetter.replyDataBuf = []byte(fmt.Sprintf(`{"result": %d}`, i+1)) } for i := 0; i < numProviders; i++ { @@ -569,8 +685,19 @@ func TestConsumerProviderTx(t *testing.T) { PairingEpoch: epoch, } } - rpcconsumerServer, _ := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) - require.NotNil(t, rpcconsumerServer) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) for i := 0; i < numProviders; i++ { replySetter := providers[i].replySetter @@ -631,7 +758,6 @@ func TestConsumerProviderJsonRpcWithNullID(t *testing.T) { specId := play.specId apiInterface := play.apiInterface epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := 5 @@ -656,7 +782,17 @@ func TestConsumerProviderJsonRpcWithNullID(t *testing.T) { ctx := context.Background() providerDataI := providers[i] listenAddress := addressGen.GetAddress() - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, []string(nil), fmt.Sprintf("provider%d", i)) + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, rpcProviderOptions) providers[i].replySetter.replyDataBuf = []byte(fmt.Sprintf(`{"result": %d}`, i+1)) } for i := 0; i < numProviders; i++ { @@ -675,8 +811,19 @@ func TestConsumerProviderJsonRpcWithNullID(t *testing.T) { PairingEpoch: epoch, } } - rpcconsumerServer, _ := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) - require.NotNil(t, rpcconsumerServer) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) for i := 0; i < numProviders; i++ { handler := func(req []byte, header http.Header) (data []byte, status int) { @@ -741,7 +888,6 @@ func TestConsumerProviderSubscriptionsHappyFlow(t *testing.T) { specId := play.specId apiInterface := play.apiInterface epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := 5 @@ -766,7 +912,17 @@ func TestConsumerProviderSubscriptionsHappyFlow(t *testing.T) { ctx := context.Background() providerDataI := providers[i] listenAddress := addressGen.GetAddress() - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, []string(nil), fmt.Sprintf("provider%d", i)) + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, rpcProviderOptions) providers[i].replySetter.replyDataBuf = []byte(fmt.Sprintf(`{"result": %d}`, i+1)) } for i := 0; i < numProviders; i++ { @@ -785,8 +941,19 @@ func TestConsumerProviderSubscriptionsHappyFlow(t *testing.T) { PairingEpoch: epoch, } } - rpcconsumerServer, _ := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) - require.NotNil(t, rpcconsumerServer) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) for i := 0; i < numProviders; i++ { handler := func(req []byte, header http.Header) (data []byte, status int) { @@ -855,7 +1022,6 @@ func TestSameProviderConflictBasicResponseCheck(t *testing.T) { specId := "LAV1" apiInterface := spectypes.APIInterfaceRest epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := play.numOfProviders @@ -883,7 +1049,17 @@ func TestSameProviderConflictBasicResponseCheck(t *testing.T) { ctx := context.Background() providerDataI := providers[i] listenAddress := addressGen.GetAddress() - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, providers[i].mockReliabilityManager = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, []string(nil), fmt.Sprintf("provider%d", i)) + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, providers[i].mockReliabilityManager = createRpcProvider(t, ctx, rpcProviderOptions) providers[i].replySetter.replyDataBuf = []byte(fmt.Sprintf(`{"result": %d}`, i+1)) } @@ -903,8 +1079,19 @@ func TestSameProviderConflictBasicResponseCheck(t *testing.T) { PairingEpoch: epoch, } } - rpcconsumerServer, _ := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) - require.NotNil(t, rpcconsumerServer) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) // Set first provider as a "liar", to return wrong block hashes getLatestBlockDataWrapper := func(rmi rpcprovider.ReliabilityManagerInf, fromBlock, toBlock, specificBlock int64) (int64, []*chaintracker.BlockStore, time.Time, error) { @@ -991,7 +1178,6 @@ func TestArchiveProvidersRetry(t *testing.T) { specId := "LAV1" apiInterface := spectypes.APIInterfaceRest epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := play.numOfProviders @@ -1023,7 +1209,18 @@ func TestArchiveProvidersRetry(t *testing.T) { if i+1 <= play.archiveProviders { addons = []string{"archive"} } - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, providers[i].mockReliabilityManager = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, addons, fmt.Sprintf("provider%d", i)) + + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: addons, + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, providers[i].mockReliabilityManager = createRpcProvider(t, ctx, rpcProviderOptions) providers[i].replySetter.replyDataBuf = []byte(`{"result": "success"}`) if i+1 <= play.nodeErrorProviders { providers[i].replySetter.replyDataBuf = []byte(`{"error": "failure", "message": "test", "code": "-32132"}`) @@ -1047,8 +1244,19 @@ func TestArchiveProvidersRetry(t *testing.T) { PairingEpoch: epoch, } } - rpcconsumerServer, _ := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) - require.NotNil(t, rpcconsumerServer) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) client := http.Client{Timeout: 1000 * time.Millisecond} req, err := http.NewRequest(http.MethodGet, "http://"+consumerListenAddress+"/lavanet/lava/conflict/params", nil) @@ -1095,7 +1303,17 @@ func TestSameProviderConflictReport(t *testing.T) { ctx := context.Background() providerDataI := providers[i] listenAddress := addressGen.GetAddress() - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, providers[i].mockReliabilityManager = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, []string(nil), fmt.Sprintf("provider%d", i)) + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, providers[i].mockReliabilityManager = createRpcProvider(t, ctx, rpcProviderOptions) providers[i].replySetter.replyDataBuf = []byte(fmt.Sprintf(`{"result": %d}`, i+1)) } } @@ -1129,7 +1347,6 @@ func TestSameProviderConflictReport(t *testing.T) { specId := "LAV1" apiInterface := spectypes.APIInterfaceRest epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := 1 @@ -1140,8 +1357,17 @@ func TestSameProviderConflictReport(t *testing.T) { initProvidersData(consumerAccount, providers, specId, apiInterface, lavaChainID) - pairingList := initPairingList(providers, epoch) - rpcconsumerServer, mockConsumerStateTracker := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: initPairingList(providers, epoch), + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) conflictSent := false wg := sync.WaitGroup{} @@ -1165,8 +1391,8 @@ func TestSameProviderConflictReport(t *testing.T) { conflictSent = true return nil } - mockConsumerStateTracker.SetTxConflictDetectionWrapper(txConflictDetectionMock) - require.NotNil(t, rpcconsumerServer) + rpcConsumerOut.mockConsumerStateTracker.SetTxConflictDetectionWrapper(txConflictDetectionMock) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) // Set first provider as a "liar", to return wrong block hashes getLatestBlockDataWrapper := func(rmi rpcprovider.ReliabilityManagerInf, fromBlock, toBlock, specificBlock int64) (int64, []*chaintracker.BlockStore, time.Time, error) { @@ -1202,7 +1428,6 @@ func TestSameProviderConflictReport(t *testing.T) { specId := "LAV1" apiInterface := spectypes.APIInterfaceRest epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := 2 @@ -1212,8 +1437,17 @@ func TestSameProviderConflictReport(t *testing.T) { initProvidersData(consumerAccount, providers, specId, apiInterface, lavaChainID) - pairingList := initPairingList(providers, epoch) - rpcconsumerServer, mockConsumerStateTracker := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: initPairingList(providers, epoch), + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) twoProvidersConflictSent := false sameProviderConflictSent := false @@ -1244,8 +1478,8 @@ func TestSameProviderConflictReport(t *testing.T) { reported <- true return nil } - mockConsumerStateTracker.SetTxConflictDetectionWrapper(txConflictDetectionMock) - require.NotNil(t, rpcconsumerServer) + rpcConsumerOut.mockConsumerStateTracker.SetTxConflictDetectionWrapper(txConflictDetectionMock) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) // Set first provider as a "liar", to return wrong block hashes getLatestBlockDataWrapper := func(rmi rpcprovider.ReliabilityManagerInf, fromBlock, toBlock, specificBlock int64) (int64, []*chaintracker.BlockStore, time.Time, error) { @@ -1285,7 +1519,6 @@ func TestConsumerProviderStatic(t *testing.T) { specId := "LAV1" apiInterface := spectypes.APIInterfaceTendermintRPC epoch := uint64(100) - requiredResponses := 1 lavaChainID := "lava" numProviders := 1 @@ -1311,7 +1544,17 @@ func TestConsumerProviderStatic(t *testing.T) { ctx := context.Background() providerDataI := providers[i] listenAddress := addressGen.GetAddress() - providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, consumerAccount.Addr.String(), specId, apiInterface, listenAddress, providerDataI.account, lavaChainID, []string(nil), fmt.Sprintf("provider%d", i)) + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: providerDataI.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: fmt.Sprintf("provider%d", i), + } + providers[i].server, providers[i].endpoint, providers[i].replySetter, providers[i].mockChainFetcher, _ = createRpcProvider(t, ctx, rpcProviderOptions) } // provider is static for i := 0; i < numProviders; i++ { @@ -1331,8 +1574,19 @@ func TestConsumerProviderStatic(t *testing.T) { StaticProvider: true, } } - rpcconsumerServer, _ := createRpcConsumer(t, ctx, specId, apiInterface, consumerAccount, consumerListenAddress, epoch, pairingList, requiredResponses, lavaChainID) - require.NotNil(t, rpcconsumerServer) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) client := http.Client{} // consumer sends the relay to a provider with an address BANANA+%d so the provider needs to skip validations for this to work resp, err := client.Get("http://" + consumerListenAddress + "/status") @@ -1351,3 +1605,282 @@ func TestConsumerProviderStatic(t *testing.T) { require.Equal(t, providers[0].replySetter.replyDataBuf, bodyBytes) resp.Body.Close() } + +func jsonRpcIdToInt(t *testing.T, rawID json.RawMessage) int { + var idInterface interface{} + err := json.Unmarshal(rawID, &idInterface) + require.NoError(t, err) + + id, ok := idInterface.(float64) + require.True(t, ok, idInterface) + return int(id) +} + +func waitForCondition(timeout, interval time.Duration, condition func() bool) bool { + ticker := time.NewTicker(interval) + defer ticker.Stop() + + timeoutTimer := time.NewTimer(timeout) + defer timeoutTimer.Stop() + + for { + select { + case <-ticker.C: + if condition() { + return true + } + case <-timeoutTimer.C: + return false + } + } +} + +func TestConsumerProviderWithConsumerSideCache(t *testing.T) { + ctx := context.Background() + // can be any spec and api interface + specId := "LAV1" + apiInterface := spectypes.APIInterfaceTendermintRPC + epoch := uint64(100) + lavaChainID := "lava" + + consumerListenAddress := addressGen.GetAddress() + cacheListenAddress := addressGen.GetAddress() + pairingList := map[uint64]*lavasession.ConsumerSessionsWithProvider{} + type providerData struct { + account sigs.Account + endpoint *lavasession.RPCProviderEndpoint + replySetter *ReplySetter + } + + consumerAccount := sigs.GenerateDeterministicFloatingKey(randomizer) + providerAccount := sigs.GenerateDeterministicFloatingKey(randomizer) + provider := providerData{account: providerAccount} + listenAddress := addressGen.GetAddress() + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: listenAddress, + account: provider.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: "provider", + } + + var mockChainFetcher *MockChainFetcher + _, provider.endpoint, provider.replySetter, mockChainFetcher, _ = createRpcProvider(t, ctx, rpcProviderOptions) + provider.replySetter.handler = func(req []byte, header http.Header) (data []byte, status int) { + var jsonRpcMessage rpcInterfaceMessages.JsonrpcMessage + err := json.Unmarshal(req, &jsonRpcMessage) + require.NoError(t, err, req) + + response := fmt.Sprintf(`{"jsonrpc":"2.0","result": {}, "id": %v}`, string(jsonRpcMessage.ID)) + return []byte(response), http.StatusOK + } + + pairingList[0] = &lavasession.ConsumerSessionsWithProvider{ + PublicLavaAddress: provider.account.Addr.String(), + Endpoints: []*lavasession.Endpoint{ + { + NetworkAddress: provider.endpoint.NetworkAddress.Address, + Enabled: true, + Geolocation: 1, + }, + }, + Sessions: map[int64]*lavasession.SingleConsumerSession{}, + MaxComputeUnits: 10000, + UsedComputeUnits: 0, + PairingEpoch: epoch, + } + + createCacheServer(t, ctx, cacheListenAddress) + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + cacheListenAddress: cacheListenAddress, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) + + client := http.Client{} + id := 0 + sendMessage := func(method string, params []string) http.Header { + // Get latest block + body := fmt.Sprintf(`{"jsonrpc":"2.0","method":"%v","params": [%v], "id":%v}`, method, strings.Join(params, ","), id) + resp, err := client.Post("http://"+consumerListenAddress, "application/json", bytes.NewBuffer([]byte(body))) + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + + bodyBytes, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + var jsonRpcMessage rpcInterfaceMessages.JsonrpcMessage + err = json.Unmarshal(bodyBytes, &jsonRpcMessage) + require.NoError(t, err) + + respId := jsonRpcIdToInt(t, jsonRpcMessage.ID) + require.Equal(t, id, respId) + resp.Body.Close() + id++ + + return resp.Header + } + + mockChainFetcher.SetBlock(1000) + + // Get latest for sanity check + providerAddr := provider.account.Addr.String() + headers := sendMessage("status", []string{}) + require.Equal(t, providerAddr, headers.Get(common.PROVIDER_ADDRESS_HEADER_NAME)) + + // Get block, this should be cached for next time + headers = sendMessage("block", []string{"1000"}) + require.Equal(t, providerAddr, headers.Get(common.PROVIDER_ADDRESS_HEADER_NAME)) + + repliedWithCache := waitForCondition(2*time.Second, 100*time.Millisecond, func() bool { + headers = sendMessage("block", []string{"1000"}) + return headers.Get(common.PROVIDER_ADDRESS_HEADER_NAME) == "Cached" + }) + require.True(t, repliedWithCache) + + // This should be from cache + headers = sendMessage("block", []string{"1000"}) + require.Equal(t, "Cached", headers.Get(common.PROVIDER_ADDRESS_HEADER_NAME)) +} + +func TestConsumerProviderWithProviderSideCache(t *testing.T) { + ctx := context.Background() + // can be any spec and api interface + specId := "LAV1" + apiInterface := spectypes.APIInterfaceTendermintRPC + epoch := uint64(100) + lavaChainID := "lava" + + consumerListenAddress := addressGen.GetAddress() + cacheListenAddress := addressGen.GetAddress() + pairingList := map[uint64]*lavasession.ConsumerSessionsWithProvider{} + type providerData struct { + account sigs.Account + endpoint *lavasession.RPCProviderEndpoint + replySetter *ReplySetter + } + + consumerAccount := sigs.GenerateDeterministicFloatingKey(randomizer) + providerAccount := sigs.GenerateDeterministicFloatingKey(randomizer) + provider := providerData{account: providerAccount} + providerListenAddress := addressGen.GetAddress() + + createCacheServer(t, ctx, cacheListenAddress) + testJsonRpcId := 42 + nodeRequestsCounter := 0 + rpcProviderOptions := rpcProviderOptions{ + consumerAddress: consumerAccount.Addr.String(), + specId: specId, + apiInterface: apiInterface, + listenAddress: providerListenAddress, + account: provider.account, + lavaChainID: lavaChainID, + addons: []string(nil), + providerUniqueId: "provider", + cacheListenAddress: cacheListenAddress, + } + + var mockChainFetcher *MockChainFetcher + _, provider.endpoint, provider.replySetter, mockChainFetcher, _ = createRpcProvider(t, ctx, rpcProviderOptions) + provider.replySetter.handler = func(req []byte, header http.Header) (data []byte, status int) { + var jsonRpcMessage rpcInterfaceMessages.JsonrpcMessage + err := json.Unmarshal(req, &jsonRpcMessage) + require.NoError(t, err, req) + + reqId := jsonRpcIdToInt(t, jsonRpcMessage.ID) + if reqId == testJsonRpcId { + nodeRequestsCounter++ + } + + response := fmt.Sprintf(`{"jsonrpc":"2.0","result": {}, "id": %v}`, string(jsonRpcMessage.ID)) + return []byte(response), http.StatusOK + } + + pairingList[0] = &lavasession.ConsumerSessionsWithProvider{ + PublicLavaAddress: provider.account.Addr.String(), + Endpoints: []*lavasession.Endpoint{ + { + NetworkAddress: provider.endpoint.NetworkAddress.Address, + Enabled: true, + Geolocation: 1, + }, + }, + Sessions: map[int64]*lavasession.SingleConsumerSession{}, + MaxComputeUnits: 10000, + UsedComputeUnits: 0, + PairingEpoch: epoch, + } + + rpcConsumerOptions := rpcConsumerOptions{ + specId: specId, + apiInterface: apiInterface, + account: consumerAccount, + consumerListenAddress: consumerListenAddress, + epoch: epoch, + pairingList: pairingList, + requiredResponses: 1, + lavaChainID: lavaChainID, + } + rpcConsumerOut := createRpcConsumer(t, ctx, rpcConsumerOptions) + require.NotNil(t, rpcConsumerOut.rpcConsumerServer) + + client := http.Client{} + sendMessage := func(method string, params []string) http.Header { + body := fmt.Sprintf(`{"jsonrpc":"2.0","method":"%v","params": [%v], "id":%v}`, method, strings.Join(params, ","), testJsonRpcId) + resp, err := client.Post("http://"+consumerListenAddress, "application/json", bytes.NewBuffer([]byte(body))) + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + + bodyBytes, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + var jsonRpcMessage rpcInterfaceMessages.JsonrpcMessage + err = json.Unmarshal(bodyBytes, &jsonRpcMessage) + require.NoError(t, err) + + respId := jsonRpcIdToInt(t, jsonRpcMessage.ID) + require.Equal(t, testJsonRpcId, respId) + resp.Body.Close() + + return resp.Header + } + + mockChainFetcher.SetBlock(1000) + + // Get latest for sanity check + sendMessage("status", []string{}) + + // Get block, this should be cached for next time + sendMessage("block", []string{"1000"}) + + timesSentMessage := 2 + + hitCache := waitForCondition(2*time.Second, 100*time.Millisecond, func() bool { + // Get block, at some point it should be from cache + sendMessage("block", []string{"1000"}) + timesSentMessage++ + return timesSentMessage > nodeRequestsCounter + }) + require.True(t, hitCache) + + // Get block again, this time it should be from cache + sendMessage("block", []string{"1000"}) + timesSentMessage++ + + cacheHits := timesSentMessage - nodeRequestsCounter + + // Verify that overall we have 2 cache hits + require.Equal(t, 2, cacheHits) +} diff --git a/protocol/metrics/metrics_consumer_manager.go b/protocol/metrics/metrics_consumer_manager.go index 2f3337e432..ce88f2145b 100644 --- a/protocol/metrics/metrics_consumer_manager.go +++ b/protocol/metrics/metrics_consumer_manager.go @@ -13,6 +13,12 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) +const ( + WsDisconnectionReasonConsumer = "consumer-disconnect" + WsDisconnectionReasonProvider = "provider-disconnect" + WsDisconnectionReasonUser = "user-disconnect" +) + type LatencyTracker struct { AverageLatency time.Duration // in nano seconds (time.Since result) TotalRequests int @@ -34,6 +40,11 @@ type ConsumerMetricsManager struct { totalNodeErroredRecoveryAttemptsMetric *prometheus.CounterVec totalRelaysSentToProvidersMetric *prometheus.CounterVec totalRelaysSentByNewBatchTickerMetric *prometheus.CounterVec + totalWsSubscriptionRequestsMetric *prometheus.CounterVec + totalFailedWsSubscriptionRequestsMetric *prometheus.CounterVec + totalWsSubscriptionDissconnectMetric *prometheus.CounterVec + totalDuplicatedWsSubscriptionRequestsMetric *prometheus.CounterVec + totalWebSocketConnectionsActive *prometheus.GaugeVec blockMetric *prometheus.GaugeVec latencyMetric *prometheus.GaugeVec qosMetric *prometheus.GaugeVec @@ -88,6 +99,31 @@ func NewConsumerMetricsManager(options ConsumerMetricsManagerOptions) *ConsumerM Help: "The total number of errors encountered by the consumer over time.", }, []string{"spec", "apiInterface"}) + totalWsSubscriptionRequestsMetric := prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "lava_consumer_total_ws_subscription_requests", + Help: "The total number of websocket subscription requests over time per chain id per api interface.", + }, []string{"spec", "apiInterface"}) + + totalFailedWsSubscriptionRequestsMetric := prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "lava_consumer_total_failed_ws_subscription_requests", + Help: "The total number of failed websocket subscription requests over time per chain id per api interface.", + }, []string{"spec", "apiInterface"}) + + totalDuplicatedWsSubscriptionRequestsMetric := prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "lava_consumer_total_duplicated_ws_subscription_requests", + Help: "The total number of duplicated webscket subscription requests over time per chain id per api interface.", + }, []string{"spec", "apiInterface"}) + + totalWebSocketConnectionsActive := prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "lava_consumer_total_websocket_connections_active", + Help: "The total number of currently active websocket connections with users", + }, []string{"spec", "apiInterface"}) + + totalWsSubscriptionDissconnectMetric := prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "lava_consumer_total_ws_subscription_disconnect", + Help: "The total number of websocket subscription disconnects over time per chain id per api interface per dissconnect reason.", + }, []string{"spec", "apiInterface", "dissconectReason"}) + blockMetric := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "lava_latest_block", Help: "The latest block measured", @@ -188,6 +224,7 @@ func NewConsumerMetricsManager(options ConsumerMetricsManagerOptions) *ConsumerM prometheus.MustRegister(endpointsHealthChecksOkMetric) prometheus.MustRegister(protocolVersionMetric) prometheus.MustRegister(totalRelaysSentByNewBatchTickerMetric) + prometheus.MustRegister(totalWebSocketConnectionsActive) prometheus.MustRegister(apiSpecificsMetric) prometheus.MustRegister(averageLatencyMetric) prometheus.MustRegister(totalRelaysSentToProvidersMetric) @@ -196,10 +233,19 @@ func NewConsumerMetricsManager(options ConsumerMetricsManagerOptions) *ConsumerM prometheus.MustRegister(totalNodeErroredRecoveryAttemptsMetric) prometheus.MustRegister(relayProcessingLatencyBeforeProvider) prometheus.MustRegister(relayProcessingLatencyAfterProvider) + prometheus.MustRegister(totalWsSubscriptionRequestsMetric) + prometheus.MustRegister(totalFailedWsSubscriptionRequestsMetric) + prometheus.MustRegister(totalDuplicatedWsSubscriptionRequestsMetric) + prometheus.MustRegister(totalWsSubscriptionDissconnectMetric) consumerMetricsManager := &ConsumerMetricsManager{ totalCURequestedMetric: totalCURequestedMetric, totalRelaysRequestedMetric: totalRelaysRequestedMetric, + totalWsSubscriptionRequestsMetric: totalWsSubscriptionRequestsMetric, + totalFailedWsSubscriptionRequestsMetric: totalFailedWsSubscriptionRequestsMetric, + totalDuplicatedWsSubscriptionRequestsMetric: totalDuplicatedWsSubscriptionRequestsMetric, + totalWsSubscriptionDissconnectMetric: totalWsSubscriptionDissconnectMetric, + totalWebSocketConnectionsActive: totalWebSocketConnectionsActive, totalErroredMetric: totalErroredMetric, blockMetric: blockMetric, latencyMetric: latencyMetric, @@ -259,6 +305,17 @@ func (pme *ConsumerMetricsManager) SetRelaySentToProviderMetric(chainId string, pme.totalRelaysSentToProvidersMetric.WithLabelValues(chainId, apiInterface).Inc() } +func (pme *ConsumerMetricsManager) SetWebSocketConnectionActive(chainId string, apiInterface string, add bool) { + if pme == nil { + return + } + if add { + pme.totalWebSocketConnectionsActive.WithLabelValues(chainId, apiInterface).Add(1) + } else { + pme.totalWebSocketConnectionsActive.WithLabelValues(chainId, apiInterface).Sub(1) + } +} + func (pme *ConsumerMetricsManager) SetRelayNodeErrorMetric(chainId string, apiInterface string) { if pme == nil { return @@ -460,3 +517,31 @@ func SetVersionInner(protocolVersionMetric *prometheus.GaugeVec, version string) combined := major*1000000 + minor*1000 + patch protocolVersionMetric.WithLabelValues("version").Set(float64(combined)) } + +func (pme *ConsumerMetricsManager) SetWsSubscriptionRequestMetric(chainId string, apiInterface string) { + if pme == nil { + return + } + pme.totalWsSubscriptionRequestsMetric.WithLabelValues(chainId, apiInterface).Inc() +} + +func (pme *ConsumerMetricsManager) SetFailedWsSubscriptionRequestMetric(chainId string, apiInterface string) { + if pme == nil { + return + } + pme.totalFailedWsSubscriptionRequestsMetric.WithLabelValues(chainId, apiInterface).Inc() +} + +func (pme *ConsumerMetricsManager) SetDuplicatedWsSubscriptionRequestMetric(chainId string, apiInterface string) { + if pme == nil { + return + } + pme.totalDuplicatedWsSubscriptionRequestsMetric.WithLabelValues(chainId, apiInterface).Inc() +} + +func (pme *ConsumerMetricsManager) SetWsSubscriptioDisconnectRequestMetric(chainId string, apiInterface string, disconnectReason string) { + if pme == nil { + return + } + pme.totalWsSubscriptionDissconnectMetric.WithLabelValues(chainId, apiInterface, disconnectReason).Inc() +} diff --git a/protocol/metrics/rpcconsumerlogs.go b/protocol/metrics/rpcconsumerlogs.go index 606ce8f114..dd5c36a3fd 100644 --- a/protocol/metrics/rpcconsumerlogs.go +++ b/protocol/metrics/rpcconsumerlogs.go @@ -87,6 +87,10 @@ func NewRPCConsumerLogs(consumerMetricsManager *ConsumerMetricsManager, consumer return rpcConsumerLogs, err } +func (rpccl *RPCConsumerLogs) SetWebSocketConnectionActive(chainId string, apiInterface string, add bool) { + rpccl.consumerMetricsManager.SetWebSocketConnectionActive(chainId, apiInterface, add) +} + func (rpccl *RPCConsumerLogs) SetRelaySentToProviderMetric(chainId string, apiInterface string) { rpccl.consumerMetricsManager.SetRelaySentToProviderMetric(chainId, apiInterface) } @@ -139,13 +143,14 @@ func (rpccl *RPCConsumerLogs) AnalyzeWebSocketErrorAndGetFormattedMessage(webSoc } rpccl.LogRequestAndResponse(rpcType+" ws msg", true, "ws", webSocketAddr, string(msg), "", msgSeed, timeTaken, err) - jsonResponse, _ := json.Marshal(fiber.Map{ + jsonResponse, err := json.Marshal(fiber.Map{ "Error_Received": rpccl.GetUniqueGuidResponseForError(err, msgSeed), }) - + if err != nil { + utils.LavaFormatError("AnalyzeWebSocketErrorAndGetFormattedMessage unexpected behavior, failed marshalling json response", err, utils.LogAttr("seed", msgSeed)) + } return jsonResponse } - return nil } diff --git a/protocol/monitoring/health.go b/protocol/monitoring/health.go index e801b9bdf3..10d68d283c 100644 --- a/protocol/monitoring/health.go +++ b/protocol/monitoring/health.go @@ -19,7 +19,6 @@ import ( "github.com/lavanet/lava/v3/protocol/rpcprovider" "github.com/lavanet/lava/v3/utils" "github.com/lavanet/lava/v3/utils/rand" - dualstakingtypes "github.com/lavanet/lava/v3/x/dualstaking/types" epochstoragetypes "github.com/lavanet/lava/v3/x/epochstorage/types" pairingtypes "github.com/lavanet/lava/v3/x/pairing/types" protocoltypes "github.com/lavanet/lava/v3/x/protocol/types" @@ -128,7 +127,7 @@ func RunHealth(ctx context.Context, errCh := make(chan error, 1) // get a list of all necessary specs for the test - dualStakingQuerier := dualstakingtypes.NewQueryClient(clientCtx) + epochstorageQuerier := epochstoragetypes.NewQueryClient(clientCtx) if getAllProviders { // var specResp *spectypes.QueryGetSpecResponse var specsResp *spectypes.QueryShowAllChainsResponse @@ -157,28 +156,23 @@ func RunHealth(ctx context.Context, defer wgproviders.Done() var err error for i := 0; i < BasicQueryRetries; i++ { - var response *dualstakingtypes.QueryDelegatorProvidersResponse + var response *epochstoragetypes.QueryProviderMetaDataResponse queryCtx, cancel := context.WithTimeout(ctx, 2*time.Second) - response, err = dualStakingQuerier.DelegatorProviders(queryCtx, &dualstakingtypes.QueryDelegatorProvidersRequest{ - Delegator: providerAddress, - WithPending: false, - }) + response, err = epochstorageQuerier.ProviderMetaData(queryCtx, &epochstoragetypes.QueryProviderMetaDataRequest{Provider: providerAddress}) cancel() if err != nil || response == nil { time.Sleep(QuerySleepTime) continue } - delegations := response.GetDelegations() - for _, delegation := range delegations { - if delegation.Provider == providerAddress { - healthResults.setSpec(&spectypes.Spec{Index: delegation.ChainID}) - for _, apiInterface := range chainIdToApiInterfaces[delegation.ChainID] { - healthResults.SetProviderData(LavaEntity{ - Address: providerAddress, - SpecId: delegation.ChainID, - ApiInterface: apiInterface, - }, ReplyData{}) - } + + for _, chain := range response.MetaData[0].Chains { + healthResults.setSpec(&spectypes.Spec{Index: chain}) + for _, apiInterface := range chainIdToApiInterfaces[chain] { + healthResults.SetProviderData(LavaEntity{ + Address: providerAddress, + SpecId: chain, + ApiInterface: apiInterface, + }, ReplyData{}) } } return diff --git a/protocol/parser/parser.go b/protocol/parser/parser.go index 19cff573f5..0eb958704f 100644 --- a/protocol/parser/parser.go +++ b/protocol/parser/parser.go @@ -50,6 +50,7 @@ func ParseDefaultBlockParameter(block string) (int64, error) { // try to parse a number } + block = unquoteString(block) hashNoPrefix, found := strings.CutPrefix(block, "0x") if len(block) >= 64 && found { if len(hashNoPrefix)%64 == 0 { @@ -96,131 +97,161 @@ func filterGenericParsersByType(genericParsers []spectypes.GenericParser, filter return retGenericParsers } -func parseInputFromParamsWithGenericParsers(rpcInput RPCInput, genericParsers []spectypes.GenericParser) (*ParsedInput, bool) { - parsedSuccessfully := false +func parseInputWithGenericParsers(rpcInput RPCInput, genericParsers []spectypes.GenericParser) (*ParsedInput, bool) { + managedToParseRawBlock := false if len(genericParsers) == 0 { - return nil, parsedSuccessfully + return nil, managedToParseRawBlock } genericParserResult, genericParserErr := ParseWithGenericParsers(rpcInput, filterGenericParsersByType(genericParsers, getParserTypeMap(PARSE_PARAMS))) if genericParserErr != nil { - return nil, parsedSuccessfully + return nil, managedToParseRawBlock } parsed := NewParsedInput() - parsedBlock := genericParserResult.GetBlock() - if parsedBlock != spectypes.NOT_APPLICABLE { - parsedSuccessfully = true - parsed.parsedBlock = parsedBlock + rawParsedData := genericParserResult.GetRawParsedData() + if rawParsedData != "" { + managedToParseRawBlock = true + parsed.parsedDataRaw = rawParsedData } parsedBlockHashes, err := genericParserResult.GetBlockHashes() if err == nil { + managedToParseRawBlock = true parsed.parsedHashes = parsedBlockHashes } - return parsed, parsedSuccessfully + return parsed, managedToParseRawBlock } -func ParseBlockFromParams(rpcInput RPCInput, blockParser spectypes.BlockParser, genericParsers []spectypes.GenericParser) *ParsedInput { - parsedBlockInfo, parsedSuccessfully := parseInputFromParamsWithGenericParsers(rpcInput, genericParsers) - if parsedSuccessfully { - return parsedBlockInfo - } - if parsedBlockInfo == nil { - parsedBlockInfo = NewParsedInput() - } - - parsedBlockInfo.parsedBlock = func() int64 { - // first we try to parse the value with the block parser - result, err := parse(rpcInput, blockParser, PARSE_PARAMS) - if err != nil || result == nil { - utils.LavaFormatDebug("ParseBlockFromParams - parse failed", - utils.LogAttr("error", err), - utils.LogAttr("result", result), - utils.LogAttr("blockParser", blockParser), - utils.LogAttr("rpcInput", rpcInput), +// ParseRawBlock attempts to parse a block from rpcInput and store it in parsedInput. +// If parsing fails or rawBlock is empty, it uses defaultValue if provided. +// If parsing the defaultValue also fails, it sets the block to NOT_APPLICABLE. +func ParseRawBlock(rpcInput RPCInput, parsedInput *ParsedInput, defaultValue string) { + rawBlock := parsedInput.GetRawParsedData() + var parsedBlock int64 + var err error + if rawBlock != "" { + parsedBlock, err = rpcInput.ParseBlock(rawBlock) + } + if rawBlock == "" || err != nil { + if defaultValue != "" { + utils.LavaFormatDebug("Failed parsing block from string, assuming default value", + utils.LogAttr("params", rpcInput.GetParams()), + utils.LogAttr("failed_parsed_value", rawBlock), + utils.LogAttr("default_value", defaultValue), ) - return spectypes.NOT_APPLICABLE - } - - resString, ok := result[0].(string) - if !ok { - utils.LavaFormatDebug("ParseBlockFromParams - result[0].(string) - type assertion failed", utils.LogAttr("result[0]", result[0])) - return spectypes.NOT_APPLICABLE - } - parsedBlock, err := rpcInput.ParseBlock(resString) - if err != nil { - if blockParser.DefaultValue != "" { - utils.LavaFormatDebug("Failed parsing block from string, assuming default value", - utils.LogAttr("params", rpcInput.GetParams()), - utils.LogAttr("failed_parsed_value", resString), - utils.LogAttr("default_value", blockParser.DefaultValue), + parsedBlock, err = rpcInput.ParseBlock(defaultValue) + if err != nil { + utils.LavaFormatError("Failed parsing default value, setting to NOT_APPLICABLE", err, + utils.LogAttr("default_value", defaultValue), ) - parsedBlock, err = rpcInput.ParseBlock(blockParser.DefaultValue) - if err != nil { - utils.LavaFormatError("Failed parsing default value, setting to NOT_APPLICABLE", err, - utils.LogAttr("default_value", blockParser.DefaultValue), - ) - return spectypes.NOT_APPLICABLE - } - } else { - return spectypes.NOT_APPLICABLE + parsedBlock = spectypes.NOT_APPLICABLE } + } else { + parsedBlock = spectypes.NOT_APPLICABLE } - return parsedBlock - }() - - return parsedBlockInfo + } + parsedInput.SetBlock(parsedBlock) } -// This returns the parsed response without decoding -func ParseFromReply(rpcInput RPCInput, blockParser spectypes.BlockParser) (string, error) { - result, err := parse(rpcInput, blockParser, PARSE_RESULT) +func parseInputWithLegacyBlockParser(rpcInput RPCInput, blockParser spectypes.BlockParser, source int) (string, error) { + result, err := legacyParse(rpcInput, blockParser, source) if err != nil || result == nil { - utils.LavaFormatDebug("ParseBlockFromParams - parse failed", + return "", utils.LavaFormatDebug("blockParsing - parse failed", utils.LogAttr("error", err), utils.LogAttr("result", result), utils.LogAttr("blockParser", blockParser), utils.LogAttr("rpcInput", rpcInput), ) - return "", err } - response, ok := result[spectypes.DEFAULT_PARSED_RESULT_INDEX].(string) + resString, ok := result[spectypes.DEFAULT_PARSED_RESULT_INDEX].(string) if !ok { - return "", utils.LavaFormatError("Failed to Convert blockData[spectypes.DEFAULT_PARSED_RESULT_INDEX].(string)", nil, utils.Attribute{Key: "blockData", Value: response[spectypes.DEFAULT_PARSED_RESULT_INDEX]}) + return "", utils.LavaFormatDebug("blockParsing - result[0].(string) - type assertion failed", utils.LogAttr("result[0]", result[0])) } - if strings.Contains(response, "\"") { - responseUnquoted, err := strconv.Unquote(response) - if err != nil { - return response, nil + return resString, nil +} + +// parseBlock processes the given RPC input using either generic parsers or a legacy block parser. +// It first attempts to parse the input with the provided generic parsers. If successful, it returns +// the parsed information after unquoting the raw parsed data. If the generic parsing fails, it falls +// back to using a legacy block parser. +// +// Parameters: +// - rpcInput: The input data to be parsed. +// - blockParser: The legacy block parser to use if generic parsing fails. +// - genericParsers: A slice of generic parsers to attempt first. +// - source: An integer representing the source of the input: either PARSE_PARAMS or PARSE_RESULT. +// +// Returns: +// - A pointer to a ParsedInput struct containing the parsed data. +func parseBlock(rpcInput RPCInput, blockParser spectypes.BlockParser, genericParsers []spectypes.GenericParser, source int) *ParsedInput { + parsedBlockInfo, _ := parseInputWithGenericParsers(rpcInput, genericParsers) + if parsedBlockInfo == nil { + parsedBlockInfo = NewParsedInput() + } else { + rawBlockFromGenericParser := parsedBlockInfo.parsedDataRaw + if rawBlockFromGenericParser != "" { + parsedBlockInfo.parsedDataRaw = unquoteString(rawBlockFromGenericParser) + return parsedBlockInfo } - return responseUnquoted, nil } - return response, nil + parsedRawBlock, _ := parseInputWithLegacyBlockParser(rpcInput, blockParser, source) + parsedBlockInfo.parsedDataRaw = unquoteString(parsedRawBlock) + return parsedBlockInfo +} + +func ParseBlockFromParams(rpcInput RPCInput, blockParser spectypes.BlockParser, genericParsers []spectypes.GenericParser) *ParsedInput { + parsedInput := parseBlock(rpcInput, blockParser, genericParsers, PARSE_PARAMS) + ParseRawBlock(rpcInput, parsedInput, blockParser.DefaultValue) + return parsedInput } -func ParseBlockFromReply(rpcInput RPCInput, blockParser spectypes.BlockParser) (int64, error) { - result, err := ParseFromReply(rpcInput, blockParser) +func ParseBlockFromReply(rpcInput RPCInput, blockParser spectypes.BlockParser, genericParsers []spectypes.GenericParser) *ParsedInput { + parsedInput := parseBlock(rpcInput, blockParser, genericParsers, PARSE_RESULT) + ParseRawBlock(rpcInput, parsedInput, blockParser.DefaultValue) + return parsedInput +} + +func unquoteString(str string) string { + if !strings.Contains(str, "\"") { + return str + } + + unquoted, err := strconv.Unquote(str) if err != nil { - return spectypes.NOT_APPLICABLE, err + return str } - return rpcInput.ParseBlock(result) + return unquoted } // This returns the parsed response after decoding -func ParseFromReplyAndDecode(rpcInput RPCInput, resultParser spectypes.BlockParser) (string, error) { - response, err := ParseFromReply(rpcInput, resultParser) +func ParseBlockHashFromReplyAndDecode(rpcInput RPCInput, resultParser spectypes.BlockParser, genericParsers []spectypes.GenericParser) (string, error) { + parsedInput, _ := parseInputWithGenericParsers(rpcInput, genericParsers) + if parsedInput == nil { + parsedBlockHashFromBlockParser, err := parseInputWithLegacyBlockParser(rpcInput, resultParser, PARSE_RESULT) + if err != nil { + return "", err + } + return parseResponseByEncoding([]byte(parsedBlockHashFromBlockParser), resultParser.Encoding) + } + + parsedBlockHashes, err := parsedInput.GetBlockHashes() if err != nil { return "", err } - return parseResponseByEncoding([]byte(response), resultParser.Encoding) + + numberOfParsedHashes := len(parsedBlockHashes) + if numberOfParsedHashes != 1 { + return "", utils.LavaFormatError("[ParseBlockHashFromReplyAndDecode] expected parsed hashes length 1", nil, utils.LogAttr("rpcInput.GetResult()", rpcInput.GetResult()), utils.LogAttr("hashes_length", numberOfParsedHashes)) + } + return parseResponseByEncoding([]byte(parsedBlockHashes[0]), resultParser.Encoding) } -func parse(rpcInput RPCInput, blockParser spectypes.BlockParser, dataSource int) ([]interface{}, error) { +func legacyParse(rpcInput RPCInput, blockParser spectypes.BlockParser, dataSource int) ([]interface{}, error) { var retval []interface{} var err error @@ -254,14 +285,18 @@ func parse(rpcInput RPCInput, blockParser spectypes.BlockParser, dataSource int) } type ParsedInput struct { - parsedBlock int64 - parsedHashes []string + parsedDataRaw string + parsedBlock int64 + parsedHashes []string } +const RAW_NOT_APPLICABLE = "-1" + func NewParsedInput() *ParsedInput { return &ParsedInput{ - parsedBlock: spectypes.NOT_APPLICABLE, - parsedHashes: make([]string, 0), + parsedDataRaw: RAW_NOT_APPLICABLE, + parsedBlock: spectypes.NOT_APPLICABLE, + parsedHashes: make([]string, 0), } } @@ -269,6 +304,10 @@ func (p *ParsedInput) SetBlock(block int64) { p.parsedBlock = block } +func (p *ParsedInput) GetRawParsedData() string { + return p.parsedDataRaw +} + func (p *ParsedInput) GetBlock() int64 { return p.parsedBlock } @@ -281,7 +320,12 @@ func (p *ParsedInput) GetBlockHashes() ([]string, error) { } func getMapForParse(rpcInput RPCInput) map[string]interface{} { - return map[string]interface{}{"params": rpcInput.GetParams(), "result": rpcInput.GetResult()} + var result map[string]interface{} + rpcInputResult := rpcInput.GetResult() + if rpcInputResult != nil { + json.Unmarshal(rpcInputResult, &result) + } + return map[string]interface{}{"params": rpcInput.GetParams(), "result": result} } func ParseWithGenericParsers(rpcInput RPCInput, genericParsers []spectypes.GenericParser) (*ParsedInput, error) { @@ -364,22 +408,14 @@ func parseGeneric(input interface{}, genericParser spectypes.GenericParser) (*Pa // regardless of the value provided by the user. for example .finality: final case spectypes.PARSER_TYPE_DEFAULT_VALUE: parsed := NewParsedInput() - block, err := ParseDefaultBlockParameter(genericParser.Value) - if err != nil { - return nil, utils.LavaFormatError("Failed converting default value to requested block", err, utils.LogAttr("genericParser.Value", genericParser.Value)) - } - parsed.parsedBlock = block + parsed.parsedDataRaw = genericParser.Value return parsed, nil // Case Block Latest, setting the value set by the user given a json path hit. // Example: block_id: 100, will result in requested block 100. case spectypes.PARSER_TYPE_BLOCK_LATEST: parsed := NewParsedInput() - valueString := blockInterfaceToString(value) - block, err := ParseDefaultBlockParameter(valueString) - if err != nil { - return nil, utils.LavaFormatWarning("Failed converting valueString to block number", err, utils.LogAttr("value", valueString)) - } - parsed.parsedBlock = block + block := blockInterfaceToString(value) + parsed.parsedDataRaw = block return parsed, nil case spectypes.PARSER_TYPE_BLOCK_HASH: return parseGenericParserBlockHash(value) diff --git a/protocol/parser/parser_test.go b/protocol/parser/parser_test.go index b90e2cd226..c005804850 100644 --- a/protocol/parser/parser_test.go +++ b/protocol/parser/parser_test.go @@ -346,8 +346,8 @@ func TestParseBlockFromParamsHappyFlow(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - block := ParseBlockFromParams(&testCase.message, testCase.blockParser, nil) - require.Equal(t, testCase.expectedBlock, block.parsedBlock) + parsedInput := ParseBlockFromParams(&testCase.message, testCase.blockParser, nil) + require.Equal(t, testCase.expectedBlock, parsedInput.GetBlock()) }) } } @@ -405,9 +405,8 @@ func TestParseBlockFromReplyHappyFlow(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - block, err := ParseBlockFromReply(&testCase.message, testCase.blockParser) - require.NoError(t, err, fmt.Sprintf("Test case name: %s", testCase.name)) - require.Equal(t, testCase.expectedBlock, block) + parsedInput := ParseBlockFromReply(&testCase.message, testCase.blockParser, nil) + require.Equal(t, testCase.expectedBlock, parsedInput.GetBlock()) }) } } @@ -585,7 +584,143 @@ func TestParseBlockFromParams(t *testing.T) { t.Run(test.name, func(t *testing.T) { t.Parallel() result := ParseBlockFromParams(test.rpcInput, test.blockParser, test.genericParsers) - require.Equal(t, test.expected, result.parsedBlock) + require.Equal(t, test.expected, result.GetBlock()) + }) + } +} + +func TestParseBlockFromReply(t *testing.T) { + tests := []struct { + name string + rpcInput RPCInput + blockParser spectypes.BlockParser + genericParsers []spectypes.GenericParser + expected int64 + }{ + { + name: "generic_parser_happy_flow_default_value", + rpcInput: &RPCInputTest{ + Result: []byte(` + { + "foo": { + "bar": [ + { + "baz": 123 + } + ] + } + } + `), + }, + genericParsers: []spectypes.GenericParser{ + { + ParsePath: ".result.foo.bar.[0].baz", + Rule: "=123", + Value: "latest", + ParseType: spectypes.PARSER_TYPE_DEFAULT_VALUE, + }, + }, + expected: spectypes.LATEST_BLOCK, + }, + { + name: "generic_parser_happy_flow_value", + rpcInput: &RPCInputTest{ + Result: []byte(` + { + "foo": { + "bar": [ + { + "baz": 123 + } + ] + } + } + `), + }, + genericParsers: []spectypes.GenericParser{ + { + ParsePath: ".result.foo.bar.[0].baz", + ParseType: spectypes.PARSER_TYPE_BLOCK_LATEST, + }, + }, + expected: 123, + }, + { + name: "generic_parser_nil_params", + rpcInput: &RPCInputTest{}, + genericParsers: []spectypes.GenericParser{ + { + ParsePath: ".result.foo", + Value: "latest", + ParseType: spectypes.PARSER_TYPE_DEFAULT_VALUE, + }, + }, + expected: spectypes.NOT_APPLICABLE, + }, + { + name: "generic_parser_fail_with_nil_var", + rpcInput: &RPCInputTest{ + Result: []byte(` + { + "bar": 123 + } + `), + }, + genericParsers: []spectypes.GenericParser{ + { + ParsePath: ".result.foo", + Value: "latest", + ParseType: spectypes.PARSER_TYPE_DEFAULT_VALUE, + }, + }, + expected: spectypes.NOT_APPLICABLE, + }, + { + name: "generic_parser_fail_with_iter_error", + rpcInput: &RPCInputTest{ + Result: []byte(` + { + "bar": 123 + } + `), + }, + genericParsers: []spectypes.GenericParser{ + { + ParsePath: ".result.bar.foo", + Value: "latest", + ParseType: spectypes.PARSER_TYPE_DEFAULT_VALUE, + }, + }, + expected: spectypes.NOT_APPLICABLE, + }, + { + name: "generic_parser_wrong_jq_path_with_parser_func_default", + rpcInput: &RPCInputTest{ + Params: map[string]interface{}{ + "bar": 123, + }, + }, + genericParsers: []spectypes.GenericParser{ + { + ParsePath: "!@#$%^&*()", + Value: "latest", + ParseType: spectypes.PARSER_TYPE_DEFAULT_VALUE, + }, + }, + blockParser: spectypes.BlockParser{ + ParserFunc: spectypes.PARSER_FUNC_DEFAULT, + ParserArg: []string{"latest"}, + }, + expected: spectypes.LATEST_BLOCK, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + parsedInput := ParseBlockFromReply(test.rpcInput, test.blockParser, test.genericParsers) + require.Equal(t, test.expected, parsedInput.GetBlock()) }) } } @@ -642,14 +777,17 @@ func TestParseBlockFromParamsHash(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - t.Parallel() - result := ParseBlockFromParams(test.rpcInput, test.blockParser, test.genericParsers) + // t.Parallel() + parsedInput := ParseBlockFromParams(test.rpcInput, test.blockParser, test.genericParsers) + parsedHashes, err := parsedInput.GetBlockHashes() if test.expectedHash == "" { - require.Len(t, result.parsedHashes, 0) + require.Error(t, err) + require.Len(t, parsedHashes, 0) } else { - require.Equal(t, test.expectedHash, result.parsedHashes[0]) + require.NoError(t, err) + require.Equal(t, test.expectedHash, parsedHashes[0]) } - require.Equal(t, test.expected, result.parsedBlock) + require.Equal(t, test.expected, parsedInput.GetBlock()) }) } } @@ -686,14 +824,14 @@ func TestParseGeneric(t *testing.T) { "finality": "final", }, } - res, err := parseGeneric(jsonMap, spectypes.GenericParser{ + parsedInput, err := parseGeneric(jsonMap, spectypes.GenericParser{ ParsePath: ".params.finality", Value: "latest", ParseType: spectypes.PARSER_TYPE_DEFAULT_VALUE, Rule: "=final || =optimistic", }) require.NoError(t, err) - require.Equal(t, spectypes.LATEST_BLOCK, res.parsedBlock) + require.Equal(t, "latest", parsedInput.GetRawParsedData()) } func TestHashLengthValidation(t *testing.T) { @@ -702,3 +840,68 @@ func TestHashLengthValidation(t *testing.T) { _, err = parseGenericParserBlockHash("123456789,123456789,123456789,12") require.NoError(t, err) } + +func TestParseRawBlock(t *testing.T) { + defaultValue := "defaultValue" + defaultBlock := int64(1) + rawBlock := "123" + expectedBlock := int64(123) + + t.Run("with raw block parsed", func(t *testing.T) { + parsedInput := &ParsedInput{ + parsedDataRaw: rawBlock, + } + + rpcInput := RPCInputTest{ + ParseBlockFunc: func(block string) (int64, error) { + require.Equal(t, parsedInput.parsedDataRaw, block) + return expectedBlock, nil + }, + } + + ParseRawBlock(&rpcInput, parsedInput, defaultValue) + require.Equal(t, expectedBlock, parsedInput.GetBlock()) + }) + + t.Run("without raw block parsed, with default value", func(t *testing.T) { + parsedInput := &ParsedInput{} + + rpcInput := RPCInputTest{ + ParseBlockFunc: func(block string) (int64, error) { + require.Equal(t, defaultValue, block) + return defaultBlock, nil + }, + } + + ParseRawBlock(&rpcInput, parsedInput, defaultValue) + require.Equal(t, defaultBlock, parsedInput.GetBlock()) + }) + + t.Run("without raw block parsed, with default value parse error", func(t *testing.T) { + parsedInput := &ParsedInput{} + + rpcInput := RPCInputTest{ + ParseBlockFunc: func(block string) (int64, error) { + require.Equal(t, defaultValue, block) + return 0, fmt.Errorf("parse error") + }, + } + + ParseRawBlock(&rpcInput, parsedInput, defaultValue) + require.Equal(t, spectypes.NOT_APPLICABLE, parsedInput.GetBlock()) + }) + + t.Run("without raw block parsed, without default value", func(t *testing.T) { + parsedInput := &ParsedInput{} + + rpcInput := RPCInputTest{ + ParseBlockFunc: func(block string) (int64, error) { + require.Fail(t, "should not be called") + return 0, nil + }, + } + + ParseRawBlock(&rpcInput, parsedInput, "") + require.Equal(t, spectypes.NOT_APPLICABLE, parsedInput.GetBlock()) + }) +} diff --git a/protocol/provideroptimizer/provider_optimizer_test.go b/protocol/provideroptimizer/provider_optimizer_test.go index 90a1553860..fb0737744d 100644 --- a/protocol/provideroptimizer/provider_optimizer_test.go +++ b/protocol/provideroptimizer/provider_optimizer_test.go @@ -261,7 +261,7 @@ func TestProviderOptimizerAvailabilityRelayData(t *testing.T) { time.Sleep(4 * time.Millisecond) results, tierResults := runChooseManyTimesAndReturnResults(t, providerOptimizer, providersGen.providersAddresses, nil, requestCU, requestBlock, 1000) require.Greater(t, tierResults[0], 300, tierResults) // 0.42 chance for top tier due to the algorithm to rebalance chances - require.Greater(t, results[providersGen.providersAddresses[skipIndex]]+results[providersGen.providersAddresses[skipIndex+1]]+results[providersGen.providersAddresses[skipIndex+2]], 300) + require.Greater(t, results[providersGen.providersAddresses[skipIndex]]+results[providersGen.providersAddresses[skipIndex+1]]+results[providersGen.providersAddresses[skipIndex+2]], 270) require.InDelta(t, results[providersGen.providersAddresses[skipIndex]], results[providersGen.providersAddresses[skipIndex+1]], 50) results, _ = runChooseManyTimesAndReturnResults(t, providerOptimizer, providersGen.providersAddresses, map[string]struct{}{providersGen.providersAddresses[skipIndex]: {}}, requestCU, requestBlock, 1000) require.Zero(t, results[providersGen.providersAddresses[skipIndex]]) diff --git a/protocol/rpcconsumer/rpcconsumer.go b/protocol/rpcconsumer/rpcconsumer.go index c919678735..3dcbe3e54e 100644 --- a/protocol/rpcconsumer/rpcconsumer.go +++ b/protocol/rpcconsumer/rpcconsumer.go @@ -298,7 +298,7 @@ func (rpcc *RPCConsumer) Start(ctx context.Context, options *rpcConsumerStartOpt if rpcEndpoint.ApiInterface == spectypes.APIInterfaceJsonRPC { specMethodType = http.MethodPost } - consumerWsSubscriptionManager = chainlib.NewConsumerWSSubscriptionManager(consumerSessionManager, rpcConsumerServer, options.refererData, specMethodType, chainParser, activeSubscriptionProvidersStorage) + consumerWsSubscriptionManager = chainlib.NewConsumerWSSubscriptionManager(consumerSessionManager, rpcConsumerServer, options.refererData, specMethodType, chainParser, activeSubscriptionProvidersStorage, consumerMetricsManager) utils.LavaFormatInfo("RPCConsumer Listening", utils.Attribute{Key: "endpoints", Value: rpcEndpoint.String()}) err = rpcConsumerServer.ServeRPCRequests(ctx, rpcEndpoint, rpcc.consumerStateTracker, chainParser, finalizationConsensus, consumerSessionManager, options.requiredResponses, privKey, lavaChainID, options.cache, rpcConsumerMetrics, consumerAddr, consumerConsistency, relaysMonitor, options.cmdFlags, options.stateShare, options.refererData, consumerReportsManager, consumerWsSubscriptionManager) @@ -618,6 +618,8 @@ rpcconsumer consumer_examples/full_consumer_example.yml --cache-be "127.0.0.1:77 cmdRPCConsumer.Flags().Float64Var(&provideroptimizer.ATierChance, common.SetProviderOptimizerBestTierPickChance, 0.75, "set the chances for picking a provider from the best group, default is 75% -> 0.75") cmdRPCConsumer.Flags().Float64Var(&provideroptimizer.LastTierChance, common.SetProviderOptimizerWorstTierPickChance, 0.0, "set the chances for picking a provider from the worse group, default is 0% -> 0.0") cmdRPCConsumer.Flags().IntVar(&provideroptimizer.OptimizerNumTiers, common.SetProviderOptimizerNumberOfTiersToCreate, 4, "set the number of groups to create, default is 4") + cmdRPCConsumer.Flags().IntVar(&chainlib.WebSocketRateLimit, common.RateLimitWebSocketFlag, chainlib.WebSocketRateLimit, "rate limit (per second) websocket requests per user connection, default is unlimited") + cmdRPCConsumer.Flags().DurationVar(&chainlib.WebSocketBanDuration, common.BanDurationForWebsocketRateLimitExceededFlag, chainlib.WebSocketBanDuration, "once websocket rate limit is reached, user will be banned Xfor a duration, default no ban") common.AddRollingLogConfig(cmdRPCConsumer) return cmdRPCConsumer } diff --git a/protocol/rpcconsumer/rpcconsumer_server.go b/protocol/rpcconsumer/rpcconsumer_server.go index 2415604d3d..ba700d6630 100644 --- a/protocol/rpcconsumer/rpcconsumer_server.go +++ b/protocol/rpcconsumer/rpcconsumer_server.go @@ -693,6 +693,7 @@ func (rpccs *RPCConsumerServer) sendRelayToProvider( utils.LavaFormatError("Failed relaySubscriptionInner", errResponse, utils.LogAttr("Request", localRelayRequestData), utils.LogAttr("Request data", string(localRelayRequestData.Data)), + utils.LogAttr("Provider", providerPublicAddress), ) } @@ -1315,6 +1316,15 @@ func (rpccs *RPCConsumerServer) appendHeadersToRelayResult(ctx context.Context, }) } + // add stateful API (hanging, transactions) + if protocolMessage.GetApi().Category.Stateful == common.CONSISTENCY_SELECT_ALL_PROVIDERS { + metadataReply = append(metadataReply, + pairingtypes.Metadata{ + Name: common.STATEFUL_API_HEADER, + Value: "true", + }) + } + // add user requested API metadataReply = append(metadataReply, pairingtypes.Metadata{ diff --git a/protocol/statetracker/updaters/epoch_updater.go b/protocol/statetracker/updaters/epoch_updater.go index 41d3fb4ed1..4f6b5f27b3 100644 --- a/protocol/statetracker/updaters/epoch_updater.go +++ b/protocol/statetracker/updaters/epoch_updater.go @@ -56,6 +56,12 @@ func NewEpochUpdater(stateQuery EpochStateQueryInterface) *EpochUpdater { func (eu *EpochUpdater) RegisterEpochUpdatable(ctx context.Context, epochUpdatable EpochUpdatable, blocksUpdateDelay int64) { eu.lock.Lock() defer eu.lock.Unlock() + // validate we do not store the same updatable twice + for _, epochUpdatableStored := range eu.epochUpdatables { + if epochUpdatable == epochUpdatableStored.EpochUpdatable { + return + } + } // initialize with the current epoch currentEpoch, err := eu.stateQuery.CurrentEpochStart(ctx) if err != nil { diff --git a/scripts/automation_scripts/short_block_proposers.py b/scripts/automation_scripts/short_block_proposers.py new file mode 100644 index 0000000000..578b3381b9 --- /dev/null +++ b/scripts/automation_scripts/short_block_proposers.py @@ -0,0 +1,50 @@ +import requests +from datetime import datetime + +def fetch_block_data(block_number): + # URL that returns a JSON response + url = 'https://lava.rest.lava.build/cosmos/base/tendermint/v1beta1/blocks/' + block_number + + # Fetch the data from the URL + response = requests.get(url) + + # Parse the JSON data + json_data = response.json() + block_header = json_data.get("sdk_block").get("header") + + height = block_header.get("height") + + dt = datetime.strptime(block_header.get("time")[:26] + "Z", "%Y-%m-%dT%H:%M:%S.%fZ") + time = dt.timestamp() + + proposer = block_header.get("proposer_address") + + return [int(height), float(time), proposer] + +stats_before = {} +stats_current = {} +current = fetch_block_data("latest") +for i in range(10000): + print(f"Progress: {i}", end='\r') + before = fetch_block_data(str(current[0]-1)) + + if current[2] not in stats_current: + stats_current[current[2]] = {"good": 0, "bad": 0} + + if before[2] not in stats_before: + stats_before[before[2]] = {"good": 0, "bad": 0} + + if current[1] - before[1] < 10: + stats_current[current[2]]["bad"] += 1 + stats_before[before[2]]["bad"] += 1 + else: + stats_current[current[2]]["good"] += 1 + stats_before[before[2]]["good"] += 1 + + current = before + +print(stats_before) +print("---------------------------------------------------------------------") +# print(stats_current) + + diff --git a/testutil/common/tester.go b/testutil/common/tester.go index 0a13df8d4f..22d4a18e48 100644 --- a/testutil/common/tester.go +++ b/testutil/common/tester.go @@ -161,6 +161,11 @@ func (ts *Tester) StakeProvider(vault string, provider string, spec spectypes.Sp return ts.StakeProviderExtra(vault, provider, spec, amount, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) } +func (ts *Tester) StakeProviderCommision(vault string, provider string, spec spectypes.Spec, amount int64, commission uint64) error { + d := MockDescription() + return ts.StakeProviderFull(vault, provider, spec, amount, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details, commission) +} + func (ts *Tester) StakeProviderExtra( vault string, provider string, @@ -173,6 +178,35 @@ func (ts *Tester) StakeProviderExtra( website string, securityContact string, descriptionDetails string, +) error { + return ts.StakeProviderFull(vault, + provider, + spec, + amount, + endpoints, + geoloc, + moniker, + identity, + website, + securityContact, + descriptionDetails, + uint64(100), + ) +} + +func (ts *Tester) StakeProviderFull( + vault string, + provider string, + spec spectypes.Spec, + amount int64, + endpoints []epochstoragetypes.Endpoint, + geoloc int32, + moniker string, + identity string, + website string, + securityContact string, + descriptionDetails string, + commission uint64, ) error { // if geoloc left zero, use default 1 if geoloc == 0 { @@ -199,7 +233,7 @@ func (ts *Tester) StakeProviderExtra( stake := sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(amount)) description := stakingtypes.NewDescription(moniker, identity, website, securityContact, descriptionDetails) - _, err := ts.TxPairingStakeProvider(vault, provider, spec.Index, stake, endpoints, geoloc, description) + _, err := ts.TxPairingStakeProvider(vault, provider, spec.Index, stake, endpoints, geoloc, description, commission) return err } @@ -352,9 +386,8 @@ func (ts *Tester) VotePeriod() uint64 { return ts.Keepers.Conflict.VotePeriod(ts.Ctx) } -func (ts *Tester) ChangeDelegationTimestamp(provider, delegator, chainID string, block uint64, timestamp int64) error { - index := dualstakingtypes.DelegationKey(provider, delegator, chainID) - return ts.Keepers.Dualstaking.ChangeDelegationTimestampForTesting(ts.Ctx, index, block, timestamp) +func (ts *Tester) ChangeDelegationTimestamp(provider, delegator string, block uint64, timestamp int64) error { + return ts.Keepers.Dualstaking.ChangeDelegationTimestampForTesting(ts.Ctx, provider, delegator, timestamp) } // proposals, transactions, queries @@ -379,7 +412,6 @@ func (ts *Tester) TxProposalAddSpecs(specs ...spectypes.Spec) error { func (ts *Tester) TxDualstakingDelegate( creator string, provider string, - chainID string, amount sdk.Coin, ) (*dualstakingtypes.MsgDelegateResponse, error) { validator, _ := ts.GetAccount(VALIDATOR, 0) @@ -387,7 +419,6 @@ func (ts *Tester) TxDualstakingDelegate( creator, sdk.ValAddress(validator.Addr).String(), provider, - chainID, amount, ) } @@ -397,14 +428,13 @@ func (ts *Tester) TxDualstakingDelegateValidator( creator string, validator string, provider string, - chainID string, amount sdk.Coin, ) (*dualstakingtypes.MsgDelegateResponse, error) { msg := &dualstakingtypes.MsgDelegate{ Creator: creator, Validator: validator, Provider: provider, - ChainID: chainID, + ChainID: "chainID", Amount: amount, } return ts.Servers.DualstakingServer.Delegate(ts.GoCtx, msg) @@ -415,16 +445,14 @@ func (ts *Tester) TxDualstakingRedelegate( creator string, fromProvider string, toProvider string, - fromChainID string, - toChainID string, amount sdk.Coin, ) (*dualstakingtypes.MsgRedelegateResponse, error) { msg := &dualstakingtypes.MsgRedelegate{ Creator: creator, FromProvider: fromProvider, ToProvider: toProvider, - FromChainID: fromChainID, - ToChainID: toChainID, + FromChainID: "fromChainID", + ToChainID: "toChainID", Amount: amount, } return ts.Servers.DualstakingServer.Redelegate(ts.GoCtx, msg) @@ -434,7 +462,6 @@ func (ts *Tester) TxDualstakingRedelegate( func (ts *Tester) TxDualstakingUnbond( creator string, provider string, - chainID string, amount sdk.Coin, ) (*dualstakingtypes.MsgUnbondResponse, error) { validator, _ := ts.GetAccount(VALIDATOR, 0) @@ -442,7 +469,6 @@ func (ts *Tester) TxDualstakingUnbond( creator, sdk.ValAddress(validator.Addr).String(), provider, - chainID, amount, ) } @@ -452,14 +478,13 @@ func (ts *Tester) TxDualstakingUnbondValidator( creator string, validator string, provider string, - chainID string, amount sdk.Coin, ) (*dualstakingtypes.MsgUnbondResponse, error) { msg := &dualstakingtypes.MsgUnbond{ Creator: creator, Validator: validator, Provider: provider, - ChainID: chainID, + ChainID: "chainID", Amount: amount, } return ts.Servers.DualstakingServer.Unbond(ts.GoCtx, msg) @@ -573,6 +598,7 @@ func (ts *Tester) TxPairingStakeProvider( endpoints []epochstoragetypes.Endpoint, geoloc int32, description stakingtypes.Description, + commission uint64, ) (*pairingtypes.MsgStakeProviderResponse, error) { val, _ := ts.GetAccount(VALIDATOR, 0) msg := &pairingtypes.MsgStakeProvider{ @@ -583,7 +609,7 @@ func (ts *Tester) TxPairingStakeProvider( Geolocation: geoloc, Endpoints: endpoints, DelegateLimit: sdk.NewCoin(ts.Keepers.StakingKeeper.BondDenom(ts.Ctx), sdk.ZeroInt()), - DelegateCommission: 100, + DelegateCommission: commission, Address: provider, Description: description, } @@ -688,6 +714,17 @@ func (ts *Tester) TxPairingUnfreezeProvider(addr, chainID string) (*pairingtypes return ts.Servers.PairingServer.UnfreezeProvider(ts.GoCtx, msg) } +// TxPairingMoveStake: implement 'tx pairing move-provider-stake' +func (ts *Tester) TxPairingMoveStake(provider, src, dst string, amount int64) (*pairingtypes.MsgMoveProviderStakeResponse, error) { + msg := &pairingtypes.MsgMoveProviderStake{ + Creator: provider, + SrcChain: src, + DstChain: dst, + Amount: NewCoin(ts.BondDenom(), amount), + } + return ts.Servers.PairingServer.MoveProviderStake(ts.GoCtx, msg) +} + func (ts *Tester) TxRewardsSetIprpcDataProposal(authority string, cost sdk.Coin, subs []string) (*rewardstypes.MsgSetIprpcDataResponse, error) { msg := rewardstypes.NewMsgSetIprpcData(authority, cost, subs) return ts.Servers.RewardsServer.SetIprpcData(ts.GoCtx, msg) @@ -885,19 +922,17 @@ func (ts *Tester) QueryPairingSubscriptionMonthlyPayout(consumer string) (*pairi } // QueryPairingVerifyPairing implements 'q dualstaking delegator-providers' -func (ts *Tester) QueryDualstakingDelegatorProviders(delegator string, withPending bool) (*dualstakingtypes.QueryDelegatorProvidersResponse, error) { +func (ts *Tester) QueryDualstakingDelegatorProviders(delegator string) (*dualstakingtypes.QueryDelegatorProvidersResponse, error) { msg := &dualstakingtypes.QueryDelegatorProvidersRequest{ - Delegator: delegator, - WithPending: withPending, + Delegator: delegator, } return ts.Keepers.Dualstaking.DelegatorProviders(ts.GoCtx, msg) } // QueryDualstakingProviderDelegators implements 'q dualstaking provider-delegators' -func (ts *Tester) QueryDualstakingProviderDelegators(provider string, withPending bool) (*dualstakingtypes.QueryProviderDelegatorsResponse, error) { +func (ts *Tester) QueryDualstakingProviderDelegators(provider string) (*dualstakingtypes.QueryProviderDelegatorsResponse, error) { msg := &dualstakingtypes.QueryProviderDelegatorsRequest{ - Provider: provider, - WithPending: withPending, + Provider: provider, } return ts.Keepers.Dualstaking.ProviderDelegators(ts.GoCtx, msg) } diff --git a/testutil/keeper/keepers_init.go b/testutil/keeper/keepers_init.go index 03a6f5f336..247aa2ab5a 100644 --- a/testutil/keeper/keepers_init.go +++ b/testutil/keeper/keepers_init.go @@ -335,8 +335,6 @@ func InitAllKeepers(t testing.TB) (*Servers, *Keepers, context.Context) { ctx = ctx.WithBlockTime(fixedDate) } - ks.Dualstaking.InitDelegations(ctx, *fixationtypes.DefaultGenesis()) - ks.Dualstaking.InitDelegators(ctx, *fixationtypes.DefaultGenesis()) ks.Plans.InitPlans(ctx, *fixationtypes.DefaultGenesis()) ks.Subscription.InitSubscriptions(ctx, *fixationtypes.DefaultGenesis()) ks.Subscription.InitSubscriptionsTimers(ctx, *timerstoretypes.DefaultGenesis()) diff --git a/utils/common/types/provider.go b/utils/common/types/provider.go index f83a43d723..7921bbbe1a 100644 --- a/utils/common/types/provider.go +++ b/utils/common/types/provider.go @@ -2,7 +2,5 @@ package types // empty provider consts const ( - EMPTY_PROVIDER = "empty_provider" - EMPTY_PROVIDER_CHAINID = "" - EMPTY_PROVIDER_CHAINID_STAR = "*" + EMPTY_PROVIDER = "empty_provider" ) diff --git a/utils/lavaslices/slices.go b/utils/lavaslices/slices.go index 747b5fc3f0..abad07d4f1 100644 --- a/utils/lavaslices/slices.go +++ b/utils/lavaslices/slices.go @@ -146,6 +146,15 @@ func Remove[T comparable](slice []T, elem T) ([]T, bool) { return slice, false } +func AddUnique[T comparable](slice []T, elem T) []T { + for _, e := range slice { + if e == elem { + return slice + } + } + return append(slice, elem) +} + func IsSubset[T comparable](subset, superset []T) bool { subsetMap := make(map[T]bool) commonMap := make(map[T]bool) diff --git a/x/conflict/keeper/conflict.go b/x/conflict/keeper/conflict.go index a3e6e31a1a..b1f5c19ae9 100644 --- a/x/conflict/keeper/conflict.go +++ b/x/conflict/keeper/conflict.go @@ -20,6 +20,11 @@ func (k Keeper) ValidateFinalizationConflict(ctx sdk.Context, conflictData *type } func (k Keeper) ValidateResponseConflict(ctx sdk.Context, conflictData *types.ResponseConflict, clientAddr sdk.AccAddress) error { + // 0. validate conflictData is not nil + if conflictData.IsDataNil() { + return fmt.Errorf("ValidateResponseConflict: conflict data is nil") + } + // 1. validate mismatching data chainID := conflictData.ConflictRelayData0.Request.RelaySession.SpecId if chainID != conflictData.ConflictRelayData1.Request.RelaySession.SpecId { @@ -279,6 +284,10 @@ func (k Keeper) ValidateSameProviderConflict(ctx sdk.Context, conflictData *type func (k Keeper) validateBlockHeights(relayFinalization *types.RelayFinalization, spec *spectypes.Spec) (finalizedBlocksMarshalled map[int64]string, earliestFinalizedBlock int64, latestFinalizedBlock int64, err error) { EMPTY_MAP := map[int64]string{} + // verify spec is not nil + if spec == nil { + return EMPTY_MAP, 0, 0, fmt.Errorf("validateBlockHeights: spec is nil") + } // Unmarshall finalized blocks finalizedBlocks := map[int64]string{} @@ -312,6 +321,9 @@ func (k Keeper) validateBlockHeights(relayFinalization *types.RelayFinalization, } func (k Keeper) validateFinalizedBlock(relayFinalization *types.RelayFinalization, latestFinalizedBlock int64, spec *spectypes.Spec) error { + if spec == nil { + return fmt.Errorf("validateFinalizedBlock: spec is nil") + } latestBlock := relayFinalization.GetLatestBlock() blockDistanceToFinalization := int64(spec.BlockDistanceForFinalizedData) diff --git a/x/conflict/keeper/msg_server_detection.go b/x/conflict/keeper/msg_server_detection.go index e9d262062d..5a2dbd3727 100644 --- a/x/conflict/keeper/msg_server_detection.go +++ b/x/conflict/keeper/msg_server_detection.go @@ -14,6 +14,9 @@ import ( "golang.org/x/exp/slices" ) +// DetectionIndex creates an index for detection instances. +// WARNING: the detection index should not be used for prefixed iteration since it doesn't contain delimeters +// thus it's not sanitized for such iterations and could cause issues in the future as the codebase evolves. func DetectionIndex(creatorAddr string, conflict *types.ResponseConflict, epochStart uint64) string { return creatorAddr + conflict.ConflictRelayData0.Request.RelaySession.Provider + conflict.ConflictRelayData1.Request.RelaySession.Provider + strconv.FormatUint(epochStart, 10) } diff --git a/x/conflict/types/conflict.go b/x/conflict/types/conflict.go new file mode 100644 index 0000000000..3b9a7c72bb --- /dev/null +++ b/x/conflict/types/conflict.go @@ -0,0 +1,24 @@ +package types + +func (c *ResponseConflict) IsDataNil() bool { + if c == nil { + return true + } + if c.ConflictRelayData0 == nil || c.ConflictRelayData1 == nil { + return true + } + if c.ConflictRelayData0.Request == nil || c.ConflictRelayData1.Request == nil { + return true + } + if c.ConflictRelayData0.Request.RelayData == nil || c.ConflictRelayData1.Request.RelayData == nil { + return true + } + if c.ConflictRelayData0.Request.RelaySession == nil || c.ConflictRelayData1.Request.RelaySession == nil { + return true + } + if c.ConflictRelayData0.Reply == nil || c.ConflictRelayData1.Reply == nil { + return true + } + + return false +} diff --git a/x/dualstaking/README.md b/x/dualstaking/README.md index 61b0cac937..682b40cc81 100644 --- a/x/dualstaking/README.md +++ b/x/dualstaking/README.md @@ -34,7 +34,7 @@ Dualstaking makes this happen by "duplicating" delegations, for each validator d ### Delegation Dualstaking introduces provider delegations to the Lava network. Provider delegations allow users to delegate their tokens to a specific provider, similar to validators, in order to contribute to their success and claim a portion of the rewards awarded to the provider. -When a provider stakes tokens, they create a self-delegation entry. Whenever a provider receives rewards, all delegators are eligible for a portion of the rewards based on their delegation amount and the commission rate set by the provider. +When a provider stakes tokens, they create a self-delegation entry. Whenever a provider receives rewards, all delegators are eligible for a portion of the rewards based on their delegation amount and the commission rate set by the provider. The delegations for a provider is split between the chains of the provider, distributed according the the providers stakes. ### Empty Provider @@ -123,9 +123,9 @@ The Dualstaking module supports the following transactions: | Transaction | Arguments | What it does | | ---------- | --------------- | ----------------------------------------------| -| `delegate` | validator-addr(string) provider-addr (string) chain-id (string) amount (coin)| delegate to validator and provider the given amount| -| `redelegate` | src-provider-addr (string) src-chain-id (string) dst-provider-addr (string) dst-chain-id (string) amount (coin)| redelegate provider delegation from source provider to destination provider| -| `unbond` | validator-addr (string) provider-addr (string) chain-id (string) amount (coin) | undong from validator and provider the given amount | +| `delegate` | validator-addr(string) provider-addr (string) amount (coin)| delegate to validator and provider the given amount| +| `redelegate` | src-provider-addr (string) dst-provider-addr (string) amount (coin)| redelegate provider delegation from source provider to destination provider| +| `unbond` | validator-addr (string) provider-addr (string) amount (coin) | undong from validator and provider the given amount | | `claim-rewards` | optional: provider-addr (string)| claim the rewards from a given provider or all rewards | diff --git a/x/dualstaking/genesis.go b/x/dualstaking/genesis.go index 5b6fecf674..ae00d5de7c 100644 --- a/x/dualstaking/genesis.go +++ b/x/dualstaking/genesis.go @@ -11,8 +11,12 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) // this line is used by starport scaffolding # genesis/module/init k.SetParams(ctx, genState.Params) - k.InitDelegations(ctx, genState.DelegationsFS) - k.InitDelegators(ctx, genState.DelegatorsFS) + for _, d := range genState.Delegations { + err := k.SetDelegation(ctx, d) + if err != nil { + panic(err) + } + } // Set all the DelegatorReward for _, elem := range genState.DelegatorRewardList { @@ -25,8 +29,12 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() genesis.Params = k.GetParams(ctx) - genesis.DelegationsFS = k.ExportDelegations(ctx) - genesis.DelegatorsFS = k.ExportDelegators(ctx) + var err error + genesis.Delegations, err = k.GetAllDelegations(ctx) + if err != nil { + panic(err) + } + genesis.DelegatorRewardList = k.GetAllDelegatorReward(ctx) // this line is used by starport scaffolding # genesis/module/export diff --git a/x/dualstaking/keeper/balance.go b/x/dualstaking/keeper/balance.go index 92292835ab..f443b2e461 100644 --- a/x/dualstaking/keeper/balance.go +++ b/x/dualstaking/keeper/balance.go @@ -17,8 +17,8 @@ func (k Keeper) BalanceDelegator(ctx sdk.Context, delegator sdk.AccAddress) (int return providers, nil } else if diff.IsPositive() { // less provider delegations,a delegation operation was done, delegate to empty provider - err = k.delegate(ctx, delegator.String(), commontypes.EMPTY_PROVIDER, commontypes.EMPTY_PROVIDER_CHAINID, - sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), diff)) + err = k.delegate(ctx, delegator.String(), commontypes.EMPTY_PROVIDER, + sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), diff), false) if err != nil { return providers, err } diff --git a/x/dualstaking/keeper/delegate.go b/x/dualstaking/keeper/delegate.go index 6e164e8c44..9b72a2bdec 100644 --- a/x/dualstaking/keeper/delegate.go +++ b/x/dualstaking/keeper/delegate.go @@ -18,8 +18,7 @@ package keeper // tracking the list of providers for a delegator, indexed by the delegator. import ( - "fmt" - + "cosmossdk.io/collections" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v3/utils" @@ -33,49 +32,24 @@ import ( // increaseDelegation increases the delegation of a delegator to a provider for a // given chain. It updates the fixation stores for both delegations and delegators, // and updates the (epochstorage) stake-entry. -func (k Keeper) increaseDelegation(ctx sdk.Context, delegator, provider, chainID string, amount sdk.Coin, nextEpoch uint64) error { - // get, update and append the delegation entry - var delegationEntry types.Delegation - index := types.DelegationKey(provider, delegator, chainID) - found := k.delegationFS.FindEntry(ctx, index, nextEpoch, &delegationEntry) - if !found { +func (k Keeper) increaseDelegation(ctx sdk.Context, delegator, provider string, amount sdk.Coin, stake bool) error { + // get, update the delegation entry + delegation, err := k.delegations.Get(ctx, types.DelegationKey(provider, delegator)) + if err != nil { // new delegation (i.e. not increase of existing one) - delegationEntry = types.NewDelegation(delegator, provider, chainID, ctx.BlockTime(), k.stakingKeeper.BondDenom(ctx)) + delegation = types.NewDelegation(delegator, provider, ctx.BlockTime(), k.stakingKeeper.BondDenom(ctx)) } - delegationEntry.AddAmount(amount) + delegation.AddAmount(amount) - err := k.delegationFS.AppendEntry(ctx, index, nextEpoch, &delegationEntry) + err = k.delegations.Set(ctx, types.DelegationKey(provider, delegator), delegation) if err != nil { - // append should never fail here - return utils.LavaFormatError("critical: append delegation entry", err, - utils.Attribute{Key: "delegator", Value: delegationEntry.Delegator}, - utils.Attribute{Key: "provider", Value: delegationEntry.Provider}, - utils.Attribute{Key: "chainID", Value: delegationEntry.ChainID}, - ) - } - - // get, update and append the delegator entry - var delegatorEntry types.Delegator - index = types.DelegatorKey(delegator) - _ = k.delegatorFS.FindEntry(ctx, index, nextEpoch, &delegatorEntry) - - contains := delegatorEntry.AddProvider(provider) - if !contains { - err = k.delegatorFS.AppendEntry(ctx, index, nextEpoch, &delegatorEntry) - if err != nil { - // append should never fail here - return utils.LavaFormatError("critical: append delegator entry", err, - utils.Attribute{Key: "delegator", Value: delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) - } + return err } if provider != commontypes.EMPTY_PROVIDER { // update the stake entry - return k.modifyStakeEntryDelegation(ctx, delegator, provider, chainID, amount, true) + return k.AfterDelegationModified(ctx, delegator, provider, amount, true, stake) } return nil @@ -84,160 +58,126 @@ func (k Keeper) increaseDelegation(ctx sdk.Context, delegator, provider, chainID // decreaseDelegation decreases the delegation of a delegator to a provider for a // given chain. It updates the fixation stores for both delegations and delegators, // and updates the (epochstorage) stake-entry. -func (k Keeper) decreaseDelegation(ctx sdk.Context, delegator, provider, chainID string, amount sdk.Coin, nextEpoch uint64) error { +func (k Keeper) decreaseDelegation(ctx sdk.Context, delegator, provider string, amount sdk.Coin, stake bool) error { // get, update and append the delegation entry - var delegationEntry types.Delegation - index := types.DelegationKey(provider, delegator, chainID) - found := k.delegationFS.FindEntry(ctx, index, nextEpoch, &delegationEntry) + delegation, found := k.GetDelegation(ctx, provider, delegator) if !found { return types.ErrDelegationNotFound } - if delegationEntry.Amount.IsLT(amount) { + if delegation.Amount.IsLT(amount) { return types.ErrInsufficientDelegation } - delegationEntry.SubAmount(amount) + delegation.SubAmount(amount) - // if delegation now becomes zero, then remove this entry altogether; - // otherwise just append the new version (for next epoch). - if delegationEntry.Amount.IsZero() { - err := k.delegationFS.DelEntry(ctx, index, nextEpoch) + if delegation.Amount.IsZero() { + err := k.RemoveDelegation(ctx, delegation) if err != nil { - // delete should never fail here - return utils.LavaFormatError("critical: delete delegation entry", err, - utils.Attribute{Key: "delegator", Value: delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) + return err } } else { - err := k.delegationFS.AppendEntry(ctx, index, nextEpoch, &delegationEntry) + err := k.SetDelegation(ctx, delegation) if err != nil { - // append should never fail here - return utils.LavaFormatError("failed to update delegation entry", err, - utils.Attribute{Key: "delegator", Value: delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) - } - } - - // get, update and append the delegator entry - var delegatorEntry types.Delegator - index = types.DelegatorKey(delegator) - found = k.delegatorFS.FindEntry(ctx, index, nextEpoch, &delegatorEntry) - if !found { - // we found the delegation above, so the delegator must exist as well - return utils.LavaFormatError("critical: delegator entry for delegation not found", - types.ErrDelegationNotFound, - utils.Attribute{Key: "delegator", Value: delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) - } - - // if delegation now becomes zero, then remove this provider from the delegator - // entry; and if the delegator entry becomes entry then remove it altogether. - // otherwise just append the new version (for next epoch). - if delegationEntry.Amount.IsZero() { - if len(k.GetAllProviderDelegatorDelegations(ctx, delegator, provider, nextEpoch)) == 0 { - delegatorEntry.DelProvider(provider) - if delegatorEntry.IsEmpty() { - err := k.delegatorFS.DelEntry(ctx, index, nextEpoch) - if err != nil { - // delete should never fail here - return utils.LavaFormatError("critical: delete delegator entry", err, - utils.Attribute{Key: "delegator", Value: delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) - } - } else { - err := k.delegatorFS.AppendEntry(ctx, index, nextEpoch, &delegatorEntry) - if err != nil { - // append should never fail here - return utils.LavaFormatError("failed to update delegator entry", err, - utils.Attribute{Key: "delegator", Value: delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) - } - } + return err } } if provider != commontypes.EMPTY_PROVIDER { - return k.modifyStakeEntryDelegation(ctx, delegator, provider, chainID, amount, false) + return k.AfterDelegationModified(ctx, delegator, provider, amount, false, stake) } return nil } -// modifyStakeEntryDelegation modifies the (epochstorage) stake-entry of the provider for a chain based on the action (increase or decrease). -func (k Keeper) modifyStakeEntryDelegation(ctx sdk.Context, delegator, provider, chainID string, amount sdk.Coin, increase bool) (err error) { - stakeEntry, exists := k.epochstorageKeeper.GetStakeEntryCurrent(ctx, chainID, provider) - if !exists || provider != stakeEntry.Address { +// this method is called after a delegation is called and redistributes the delegations among the stake entries of the provider. +// 'stake' arg needs to be true if the code reached here from pairing stake/unstake tx (this means the 'stake' field is already set) +func (k Keeper) AfterDelegationModified(ctx sdk.Context, delegator, provider string, amount sdk.Coin, increase, stake bool) (err error) { + // get all entries + metadata, err := k.epochstorageKeeper.GetMetadata(ctx, provider) + if err != nil { if increase { - return epochstoragetypes.ErrProviderNotStaked + return err + } else { + // we want to allow decreasing if the provider does not exist + return nil } - // For decrease, if the provider doesn't exist, return without error - return nil } - if delegator == stakeEntry.Vault { - if increase { - stakeEntry.Stake = stakeEntry.Stake.Add(amount) - } else { - stakeEntry.Stake, err = stakeEntry.Stake.SafeSub(amount) - if err != nil { - return fmt.Errorf("invalid or insufficient funds: %w", err) - } + // get all entries + TotalSelfDelegation := sdk.ZeroInt() + entries := []*epochstoragetypes.StakeEntry{} + for _, chain := range metadata.Chains { + entry, found := k.epochstorageKeeper.GetStakeEntryCurrent(ctx, chain, provider) + if !found { + panic("AfterDelegationModified: entry not found") } - } else { - if increase { - stakeEntry.DelegateTotal = stakeEntry.DelegateTotal.Add(amount) - } else { - stakeEntry.DelegateTotal, err = stakeEntry.DelegateTotal.SafeSub(amount) - if err != nil { - return fmt.Errorf("invalid or insufficient funds: %w", err) + + entries = append(entries, &entry) + TotalSelfDelegation = TotalSelfDelegation.Add(entry.Stake.Amount) + } + + // regular delegation + if !amount.IsZero() { + if delegator != metadata.Vault { + if increase { + metadata.TotalDelegations = metadata.TotalDelegations.Add(amount) + } else { + metadata.TotalDelegations, err = metadata.TotalDelegations.SafeSub(amount) + if err != nil { + return err + } + } + } else if !stake { + // distribute self delegations if done through the dualstaking tx + total := amount.Amount + count := int64(len(entries)) + for _, entry := range entries { + part := total.QuoRaw(count) + if increase { + entry.Stake = entry.Stake.AddAmount(part) + TotalSelfDelegation = TotalSelfDelegation.Add(part) + } else { + entry.Stake.Amount, err = entry.Stake.Amount.SafeSub(part) + if err != nil { + return err + } + TotalSelfDelegation = TotalSelfDelegation.Sub(part) + if entry.Stake.IsLT(k.GetParams(ctx).MinSelfDelegation) { + return utils.LavaFormatError("self delegation below minimum, use unstake tx", nil, utils.LogAttr("chainID", entry.Chain)) + } + } + total = total.Sub(part) + count-- } } + k.epochstorageKeeper.SetMetadata(ctx, metadata) } details := map[string]string{ - "provider_vault": stakeEntry.Vault, - "provider_provider": stakeEntry.Address, - "chain_id": stakeEntry.Chain, - "moniker": stakeEntry.Description.Moniker, - "description": stakeEntry.Description.String(), - "stake": stakeEntry.Stake.String(), - "effective_stake": stakeEntry.TotalStake().String() + stakeEntry.Stake.Denom, - } - - if stakeEntry.Stake.IsLT(k.GetParams(ctx).MinSelfDelegation) { - k.epochstorageKeeper.RemoveStakeEntryCurrent(ctx, chainID, stakeEntry.Address) - details["min_self_delegation"] = k.GetParams(ctx).MinSelfDelegation.String() - utils.LogLavaEvent(ctx, k.Logger(ctx), types.UnstakeFromUnbond, details, "unstaking provider due to unbond that lowered its stake below min self delegation") - return nil - } else if stakeEntry.TotalStake().LT(k.specKeeper.GetMinStake(ctx, chainID).Amount) { - details["min_spec_stake"] = k.specKeeper.GetMinStake(ctx, chainID).String() - utils.LogLavaEvent(ctx, k.Logger(ctx), types.FreezeFromUnbond, details, "freezing provider due to stake below min spec stake") - stakeEntry.Freeze() - } else if delegator == stakeEntry.Vault && stakeEntry.IsFrozen() && !stakeEntry.IsJailed(ctx.BlockTime().UTC().Unix()) { - stakeEntry.UnFreeze(k.epochstorageKeeper.GetCurrentNextEpoch(ctx) + 1) - } - - k.epochstorageKeeper.SetStakeEntryCurrent(ctx, stakeEntry) + "provider": provider, + } + + for _, entry := range entries { + details[entry.Chain] = entry.Chain + entry.DelegateTotal = sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), metadata.TotalDelegations.Amount.Mul(entry.Stake.Amount).Quo(TotalSelfDelegation)) + if entry.TotalStake().LT(k.specKeeper.GetMinStake(ctx, entry.Chain).Amount) { + details["min_spec_stake"] = k.specKeeper.GetMinStake(ctx, entry.Chain).String() + details["stake"] = entry.TotalStake().String() + utils.LogLavaEvent(ctx, k.Logger(ctx), types.FreezeFromUnbond, details, "freezing provider due to stake below min spec stake") + entry.Freeze() + } else if delegator == entry.Vault && entry.IsFrozen() && !entry.IsJailed(ctx.BlockTime().UTC().Unix()) { + entry.UnFreeze(k.epochstorageKeeper.GetCurrentNextEpoch(ctx) + 1) + } + k.epochstorageKeeper.SetStakeEntryCurrent(ctx, *entry) + } return nil } // delegate lets a delegator delegate an amount of coins to a provider. // (effective on next epoch) -func (k Keeper) delegate(ctx sdk.Context, delegator, provider, chainID string, amount sdk.Coin) error { - nextEpoch := k.epochstorageKeeper.GetCurrentNextEpoch(ctx) - +func (k Keeper) delegate(ctx sdk.Context, delegator, provider string, amount sdk.Coin, stake bool) error { _, err := sdk.AccAddressFromBech32(delegator) if err != nil { return utils.LavaFormatWarning("invalid delegator address", err, @@ -257,17 +197,15 @@ func (k Keeper) delegate(ctx sdk.Context, delegator, provider, chainID string, a return utils.LavaFormatWarning("failed to delegate: coin validation failed", err, utils.Attribute{Key: "delegator", Value: delegator}, utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, ) } - err = k.increaseDelegation(ctx, delegator, provider, chainID, amount, nextEpoch) + err = k.increaseDelegation(ctx, delegator, provider, amount, stake) if err != nil { return utils.LavaFormatWarning("failed to increase delegation", err, utils.Attribute{Key: "delegator", Value: delegator}, utils.Attribute{Key: "provider", Value: provider}, utils.Attribute{Key: "amount", Value: amount.String()}, - utils.Attribute{Key: "chainID", Value: chainID}, ) } @@ -277,19 +215,7 @@ func (k Keeper) delegate(ctx sdk.Context, delegator, provider, chainID string, a // Redelegate lets a delegator transfer its delegation between providers, but // without the funds being subject to unstakeHoldBlocks witholding period. // (effective on next epoch) -func (k Keeper) Redelegate(ctx sdk.Context, delegator, from, to, fromChainID, toChainID string, amount sdk.Coin) error { - _, foundFrom := k.specKeeper.GetSpec(ctx, fromChainID) - _, foundTo := k.specKeeper.GetSpec(ctx, toChainID) - if (!foundFrom && fromChainID != commontypes.EMPTY_PROVIDER_CHAINID) || - (!foundTo && toChainID != commontypes.EMPTY_PROVIDER_CHAINID) { - return utils.LavaFormatWarning("cannot redelegate with invalid chain IDs", fmt.Errorf("chain ID not found"), - utils.LogAttr("from_chain_id", fromChainID), - utils.LogAttr("to_chain_id", toChainID), - ) - } - - nextEpoch := k.epochstorageKeeper.GetCurrentNextEpoch(ctx) - +func (k Keeper) Redelegate(ctx sdk.Context, delegator, from, to string, amount sdk.Coin, stake bool) error { if _, err := sdk.AccAddressFromBech32(delegator); err != nil { return utils.LavaFormatWarning("invalid delegator address", err, utils.Attribute{Key: "delegator", Value: delegator}, @@ -319,7 +245,7 @@ func (k Keeper) Redelegate(ctx sdk.Context, delegator, from, to, fromChainID, to ) } - err := k.increaseDelegation(ctx, delegator, to, toChainID, amount, nextEpoch) + err := k.increaseDelegation(ctx, delegator, to, amount, stake) if err != nil { return utils.LavaFormatWarning("failed to increase delegation", err, utils.Attribute{Key: "delegator", Value: delegator}, @@ -328,7 +254,7 @@ func (k Keeper) Redelegate(ctx sdk.Context, delegator, from, to, fromChainID, to ) } - err = k.decreaseDelegation(ctx, delegator, from, fromChainID, amount, nextEpoch) + err = k.decreaseDelegation(ctx, delegator, from, amount, stake) if err != nil { return utils.LavaFormatWarning("failed to decrease delegation", err, utils.Attribute{Key: "delegator", Value: delegator}, @@ -348,15 +274,7 @@ func (k Keeper) Redelegate(ctx sdk.Context, delegator, from, to, fromChainID, to // before released and transferred back to the delegator. The rewards from the // provider will be updated accordingly (or terminate) from the next epoch. // (effective on next epoch) -func (k Keeper) unbond(ctx sdk.Context, delegator, provider, chainID string, amount sdk.Coin) error { - _, found := k.specKeeper.GetSpec(ctx, chainID) - if chainID != commontypes.EMPTY_PROVIDER_CHAINID && !found { - return utils.LavaFormatWarning("cannot unbond with invalid chain ID", fmt.Errorf("chain ID not found"), - utils.LogAttr("chain_id", chainID)) - } - - nextEpoch := k.epochstorageKeeper.GetCurrentNextEpoch(ctx) - +func (k Keeper) unbond(ctx sdk.Context, delegator, provider string, amount sdk.Coin, stake bool) error { if _, err := sdk.AccAddressFromBech32(delegator); err != nil { return utils.LavaFormatWarning("invalid delegator address", err, utils.Attribute{Key: "delegator", Value: delegator}, @@ -378,7 +296,7 @@ func (k Keeper) unbond(ctx sdk.Context, delegator, provider, chainID string, amo ) } - err := k.decreaseDelegation(ctx, delegator, provider, chainID, amount, nextEpoch) + err := k.decreaseDelegation(ctx, delegator, provider, amount, stake) if err != nil { return utils.LavaFormatWarning("failed to decrease delegation", err, utils.Attribute{Key: "delegator", Value: delegator}, @@ -391,7 +309,7 @@ func (k Keeper) unbond(ctx sdk.Context, delegator, provider, chainID string, amo } // GetDelegatorProviders gets all the providers the delegator is delegated to -func (k Keeper) GetDelegatorProviders(ctx sdk.Context, delegator string, epoch uint64) (providers []string, err error) { +func (k Keeper) GetDelegatorProviders(ctx sdk.Context, delegator string) (providers []string, err error) { _, err = sdk.AccAddressFromBech32(delegator) if err != nil { return nil, utils.LavaFormatWarning("cannot get delegator's providers", err, @@ -399,14 +317,23 @@ func (k Keeper) GetDelegatorProviders(ctx sdk.Context, delegator string, epoch u ) } - var delegatorEntry types.Delegator - prefix := types.DelegatorKey(delegator) - k.delegatorFS.FindEntry(ctx, prefix, epoch, &delegatorEntry) + iter, err := k.delegations.Indexes.ReverseIndex.MatchExact(ctx, delegator) + if err != nil { + return nil, err + } + + for ; iter.Valid(); iter.Next() { + key, err := iter.PrimaryKey() + if err != nil { + return nil, err + } + providers = append(providers, key.K1()) + } - return delegatorEntry.Providers, nil + return providers, nil } -func (k Keeper) GetProviderDelegators(ctx sdk.Context, provider string, epoch uint64) ([]types.Delegation, error) { +func (k Keeper) GetProviderDelegators(ctx sdk.Context, provider string) ([]types.Delegation, error) { if provider != commontypes.EMPTY_PROVIDER { _, err := sdk.AccAddressFromBech32(provider) if err != nil { @@ -416,76 +343,52 @@ func (k Keeper) GetProviderDelegators(ctx sdk.Context, provider string, epoch ui } } - var delegations []types.Delegation - indices := k.delegationFS.GetAllEntryIndicesWithPrefix(ctx, provider) - for _, ind := range indices { - var delegation types.Delegation - found := k.delegationFS.FindEntry(ctx, ind, epoch, &delegation) - if !found { - provider, delegator, chainID := types.DelegationKeyDecode(ind) - utils.LavaFormatError("delegationFS entry index has no entry", fmt.Errorf("provider delegation not found"), - utils.Attribute{Key: "delegator", Value: delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) - continue - } - delegations = append(delegations, delegation) + iter, err := k.delegations.Iterate(ctx, collections.NewPrefixedPairRange[string, string](provider)) + if err != nil { + return nil, err } - return delegations, nil + return iter.Values() } -func (k Keeper) GetDelegation(ctx sdk.Context, delegator, provider, chainID string, epoch uint64) (types.Delegation, bool) { - var delegationEntry types.Delegation - index := types.DelegationKey(provider, delegator, chainID) - found := k.delegationFS.FindEntry(ctx, index, epoch, &delegationEntry) - - return delegationEntry, found +func (k Keeper) GetDelegation(ctx sdk.Context, provider, delegator string) (types.Delegation, bool) { + delegation, err := k.delegations.Get(ctx, types.DelegationKey(provider, delegator)) + return delegation, err == nil } -func (k Keeper) GetAllProviderDelegatorDelegations(ctx sdk.Context, delegator, provider string, epoch uint64) []types.Delegation { - prefix := types.DelegationKey(provider, delegator, "") - indices := k.delegationFS.GetAllEntryIndicesWithPrefix(ctx, prefix) - - var delegations []types.Delegation - for _, ind := range indices { - var delegation types.Delegation - _, deleted, _, found := k.delegationFS.FindEntryDetailed(ctx, ind, epoch, &delegation) - if !found { - if !deleted { - provider, delegator, chainID := types.DelegationKeyDecode(ind) - utils.LavaFormatError("delegationFS entry index has no entry", fmt.Errorf("provider delegation not found"), - utils.Attribute{Key: "delegator", Value: delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) - } - continue - } - delegations = append(delegations, delegation) +func (k Keeper) GetAllDelegations(ctx sdk.Context) ([]types.Delegation, error) { + iter, err := k.delegations.Iterate(ctx, nil) + if err != nil { + return nil, err } - return delegations + return iter.Values() +} + +func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) error { + return k.delegations.Set(ctx, types.DelegationKey(delegation.Provider, delegation.Delegator), delegation) +} + +func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) error { + return k.delegations.Remove(ctx, types.DelegationKey(delegation.Provider, delegation.Delegator)) } func (k Keeper) UnbondUniformProviders(ctx sdk.Context, delegator string, amount sdk.Coin) error { - epoch := k.epochstorageKeeper.GetCurrentNextEpoch(ctx) - providers, err := k.GetDelegatorProviders(ctx, delegator, epoch) + providers, err := k.GetDelegatorProviders(ctx, delegator) if err != nil { return err } // first remove from the empty provider if lavaslices.Contains[string](providers, commontypes.EMPTY_PROVIDER) { - delegation, found := k.GetDelegation(ctx, delegator, commontypes.EMPTY_PROVIDER, commontypes.EMPTY_PROVIDER_CHAINID, epoch) + delegation, found := k.GetDelegation(ctx, commontypes.EMPTY_PROVIDER, delegator) if found { if delegation.Amount.Amount.GTE(amount.Amount) { // we have enough here, remove all from empty delegator and bail - return k.unbond(ctx, delegator, commontypes.EMPTY_PROVIDER, commontypes.EMPTY_PROVIDER_CHAINID, amount) + return k.unbond(ctx, delegator, commontypes.EMPTY_PROVIDER, amount, false) } else { // we dont have enough in the empty provider, remove everything and continue with the rest - err = k.unbond(ctx, delegator, commontypes.EMPTY_PROVIDER, commontypes.EMPTY_PROVIDER_CHAINID, delegation.Amount) + err = k.unbond(ctx, delegator, commontypes.EMPTY_PROVIDER, delegation.Amount, false) if err != nil { return err } @@ -498,30 +401,27 @@ func (k Keeper) UnbondUniformProviders(ctx sdk.Context, delegator string, amount var delegations []types.Delegation for _, provider := range providers { - delegations = append(delegations, k.GetAllProviderDelegatorDelegations(ctx, delegator, provider, epoch)...) + delegation, found := k.GetDelegation(ctx, provider, delegator) + if found { + delegations = append(delegations, delegation) + } } slices.SortFunc(delegations, func(i, j types.Delegation) bool { return i.Amount.IsLT(j.Amount) }) - type delegationKey struct { - provider string - chainID string - } - unbondAmount := map[delegationKey]sdk.Coin{} - + unbondAmount := map[string]sdk.Coin{} // first round of deduction for i := range delegations { - key := delegationKey{provider: delegations[i].Provider, chainID: delegations[i].ChainID} amountToDeduct := amount.Amount.QuoRaw(int64(len(delegations) - i)) if delegations[i].Amount.Amount.LT(amountToDeduct) { - unbondAmount[key] = delegations[i].Amount + unbondAmount[delegations[i].Provider] = delegations[i].Amount amount = amount.Sub(delegations[i].Amount) delegations[i].Amount.Amount = sdk.ZeroInt() } else { coinToDeduct := sdk.NewCoin(delegations[i].Amount.Denom, amountToDeduct) - unbondAmount[key] = coinToDeduct + unbondAmount[delegations[i].Provider] = coinToDeduct amount = amount.Sub(coinToDeduct) delegations[i].Amount = delegations[i].Amount.Sub(coinToDeduct) } @@ -532,7 +432,7 @@ func (k Keeper) UnbondUniformProviders(ctx sdk.Context, delegator string, amount if amount.IsZero() { break } - key := delegationKey{provider: delegations[i].Provider, chainID: delegations[i].ChainID} + key := delegations[i].Provider if delegations[i].Amount.Amount.LT(amount.Amount) { unbondAmount[key].Add(delegations[i].Amount) amount = amount.Sub(delegations[i].Amount) @@ -544,8 +444,8 @@ func (k Keeper) UnbondUniformProviders(ctx sdk.Context, delegator string, amount // now unbond all for i := range delegations { - key := delegationKey{provider: delegations[i].Provider, chainID: delegations[i].ChainID} - err := k.unbond(ctx, delegator, delegations[i].Provider, delegations[i].ChainID, unbondAmount[key]) + key := delegations[i].Provider + err := k.unbond(ctx, delegator, delegations[i].Provider, unbondAmount[key], false) if err != nil { return err } @@ -556,16 +456,15 @@ func (k Keeper) UnbondUniformProviders(ctx sdk.Context, delegator string, amount // returns the difference between validators delegations and provider delegation (validators-providers) func (k Keeper) VerifyDelegatorBalance(ctx sdk.Context, delAddr sdk.AccAddress) (math.Int, int, error) { - nextEpoch := k.epochstorageKeeper.GetCurrentNextEpoch(ctx) - providers, err := k.GetDelegatorProviders(ctx, delAddr.String(), nextEpoch) + providers, err := k.GetDelegatorProviders(ctx, delAddr.String()) if err != nil { return math.ZeroInt(), 0, err } sumProviderDelegations := sdk.ZeroInt() for _, p := range providers { - delegations := k.GetAllProviderDelegatorDelegations(ctx, delAddr.String(), p, nextEpoch) - for _, d := range delegations { + d, found := k.GetDelegation(ctx, p, delAddr.String()) + if found { sumProviderDelegations = sumProviderDelegations.Add(d.Amount.Amount) } } diff --git a/x/dualstaking/keeper/delegate_test.go b/x/dualstaking/keeper/delegate_test.go index 2ef090c060..0518f01771 100644 --- a/x/dualstaking/keeper/delegate_test.go +++ b/x/dualstaking/keeper/delegate_test.go @@ -44,13 +44,6 @@ func TestDelegateFail(t *testing.T) { chainID: "mockspec", amount: 1, }, - { - name: "bad chainID", - delegator: client1Addr, - provider: provider1Addr, - chainID: "invalid", - amount: 1, - }, { name: "bad amount", delegator: client1Addr, @@ -78,7 +71,7 @@ func TestDelegateFail(t *testing.T) { t.Run(tt.name, func(t *testing.T) { amount := sdk.NewCoin(commontypes.TokenDenom, sdk.ZeroInt()) amount.Amount = amount.Amount.Add(sdk.NewInt(tt.amount)) - _, err := ts.TxDualstakingDelegate(tt.delegator, tt.provider, tt.chainID, amount) + _, err := ts.TxDualstakingDelegate(tt.delegator, tt.provider, amount) require.Error(t, err, tt.name) }) } @@ -97,7 +90,7 @@ func TestDelegate(t *testing.T) { // delegate once amount := sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(10000)) - _, err := ts.TxDualstakingDelegate(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err := ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) // not yet in effect @@ -111,10 +104,10 @@ func TestDelegate(t *testing.T) { require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) // delegate twice same block (fail) - _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) - _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) stakeEntry = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) @@ -143,7 +136,7 @@ func TestRedelegateFail(t *testing.T) { // delegate once for setup amount := sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(10000)) - _, err := ts.TxDualstakingDelegate(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err := ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) template := []struct { @@ -178,14 +171,6 @@ func TestRedelegateFail(t *testing.T) { chainID: "mockspec", amount: 1, }, - { - name: "bad chainID", - delegator: client1Addr, - provider1: provider1Addr, - provider2: provider2Addr, - chainID: "invalid", - amount: 1, - }, { name: "bad amount", delegator: client1Addr, @@ -225,7 +210,7 @@ func TestRedelegateFail(t *testing.T) { amount := sdk.NewCoin(commontypes.TokenDenom, sdk.ZeroInt()) amount.Amount = amount.Amount.Add(sdk.NewInt(tt.amount)) _, err := ts.TxDualstakingRedelegate( - tt.delegator, tt.provider1, tt.provider2, tt.chainID, tt.chainID, amount) + tt.delegator, tt.provider1, tt.provider2, amount) require.Error(t, err, tt.name) }) } @@ -247,7 +232,7 @@ func TestRedelegate(t *testing.T) { // delegate once amount := sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(10000)) _, err := ts.TxDualstakingDelegate( - client1Addr, provider1Addr, ts.spec.Name, amount) + client1Addr, provider1Addr, amount) require.NoError(t, err) // advance epoch to digest the delegate @@ -262,7 +247,7 @@ func TestRedelegate(t *testing.T) { // redelegate once amount = sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(5000)) _, err = ts.TxDualstakingRedelegate( - client1Addr, provider1Addr, provider2Addr, ts.spec.Name, ts.spec.Name, amount) + client1Addr, provider1Addr, provider2Addr, amount) require.NoError(t, err) stakeEntry1 = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) stakeEntry2 = ts.getStakeEntry(provider2Acct.Addr.String(), ts.spec.Name) @@ -283,8 +268,7 @@ func TestRedelegate(t *testing.T) { // redelegate from unstaking provider amount = sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(5000)) - _, err = ts.TxDualstakingRedelegate( - client1Addr, provider1Addr, provider2Addr, ts.spec.Name, ts.spec.Name, amount) + _, err = ts.TxDualstakingRedelegate(client1Addr, provider1Addr, provider2Addr, amount) require.NoError(t, err) // advance epoch to digest the delegate ts.AdvanceEpoch() @@ -308,7 +292,7 @@ func TestUnbondFail(t *testing.T) { // delegate once for setup amount := sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(10000)) - _, err := ts.TxDualstakingDelegate(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err := ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) template := []struct { @@ -332,13 +316,6 @@ func TestUnbondFail(t *testing.T) { chainID: "mockspec", amount: 1, }, - { - name: "bad chainID", - delegator: client1Addr, - provider: provider1Addr, - chainID: "invalid", - amount: 1, - }, { name: "bad amount", delegator: client1Addr, @@ -366,7 +343,7 @@ func TestUnbondFail(t *testing.T) { t.Run(tt.name, func(t *testing.T) { amount := sdk.NewCoin(commontypes.TokenDenom, sdk.ZeroInt()) amount.Amount = amount.Amount.Add(sdk.NewInt(tt.amount)) - _, err := ts.TxDualstakingUnbond(tt.delegator, tt.provider, tt.chainID, amount) + _, err := ts.TxDualstakingUnbond(tt.delegator, tt.provider, amount) require.Error(t, err, tt.name) }) } @@ -392,69 +369,68 @@ func TestUnbond(t *testing.T) { // delegate once amount := sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(10000)) - _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) - _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, spec2.Index, amount) + _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) // advance epoch to digest the delegate ts.AdvanceEpoch() // now in effect - delegated = delegated.Add(amount) + delegated = delegated.Add(amount).Add(amount) stakeEntry := ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) - require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) + require.True(t, delegated.Amount.QuoRaw(2).Equal(stakeEntry.DelegateTotal.Amount)) - res, err := ts.QueryDualstakingDelegatorProviders(client1Addr, true) + res, err := ts.QueryDualstakingDelegatorProviders(client1Addr) require.NoError(t, err) - require.Len(t, res.Delegations, 2) + require.Len(t, res.Delegations, 1) // unbond once amount = sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(1000)) - _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, amount) require.NoError(t, err) stakeEntry = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) - require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) - + require.True(t, delegated.Amount.QuoRaw(2).Equal(stakeEntry.DelegateTotal.Amount)) // advance epoch to digest the delegate ts.AdvanceEpoch() // now in effect delegated = delegated.Sub(amount) stakeEntry = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) - require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) + require.True(t, delegated.Amount.QuoRaw(2).Equal(stakeEntry.DelegateTotal.Amount)) // unbond twice in same block, and then in next block - _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, amount) require.NoError(t, err) // _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, ts.spec.Name, amount) // require.Error(t, err) ts.AdvanceBlock() - _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, amount) require.NoError(t, err) stakeEntry = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) - require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) + require.True(t, delegated.Amount.QuoRaw(2).Equal(stakeEntry.DelegateTotal.Amount)) // advance epoch to digest the delegate ts.AdvanceEpoch() // now in effect delegated = delegated.Sub(amount).Sub(amount) stakeEntry = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) - require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) + require.True(t, delegated.Amount.QuoRaw(2).Equal(stakeEntry.DelegateTotal.Amount)) _, err = ts.TxPairingUnstakeProvider(provider1Acct.GetVaultAddr(), ts.spec.Name) require.NoError(t, err) // unbond from unstaking provider - _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, amount) require.NoError(t, err) stakeEntry = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) - require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) + require.True(t, delegated.Amount.QuoRaw(2).Equal(stakeEntry.DelegateTotal.Amount)) delegated = delegated.Sub(amount) // unbond from unstaking provider everything - _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, ts.spec.Name, delegated) + _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, delegated) require.NoError(t, err) stakeEntry = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) @@ -469,10 +445,9 @@ func TestUnbond(t *testing.T) { ts.verifyDelegatorsBalance() - res, err = ts.QueryDualstakingDelegatorProviders(client1Addr, true) + res, err = ts.QueryDualstakingDelegatorProviders(client1Addr) require.NoError(t, err) - require.Len(t, res.Delegations, 1) - require.Equal(t, provider1Addr, res.Delegations[0].Provider) + require.Len(t, res.Delegations, 0) } func TestBondUnbondBond(t *testing.T) { @@ -488,7 +463,7 @@ func TestBondUnbondBond(t *testing.T) { // delegate once amount := sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(10000)) - _, err := ts.TxDualstakingDelegate(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err := ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) // advance epoch to digest the delegate @@ -499,7 +474,7 @@ func TestBondUnbondBond(t *testing.T) { require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) // unbond once - _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingUnbond(client1Addr, provider1Addr, amount) require.NoError(t, err) stakeEntry = ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) @@ -512,7 +487,7 @@ func TestBondUnbondBond(t *testing.T) { require.True(t, delegated.IsEqual(stakeEntry.DelegateTotal)) // delegate second time - _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, ts.spec.Name, amount) + _, err = ts.TxDualstakingDelegate(client1Addr, provider1Addr, amount) require.NoError(t, err) ts.AdvanceEpoch() @@ -533,7 +508,7 @@ func TestDualstakingUnbondStakeIsLowerThanMinStakeCausesFreeze(t *testing.T) { amountToUnbond := staked.Sub(minSelfDelegation.AddAmount(math.OneInt())) // unbond once (not unstaking completely but still below min stake) - _, err := ts.TxDualstakingUnbond(provider1Acct.GetVaultAddr(), provider, ts.spec.Name, amountToUnbond) + _, err := ts.TxDualstakingUnbond(provider1Acct.GetVaultAddr(), provider, amountToUnbond) require.NoError(t, err) stakeEntry := ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) @@ -548,7 +523,7 @@ func TestDualstakingUnbondStakeIsLowerThanMinStakeCausesFreeze(t *testing.T) { require.True(t, stakeEntry.IsFrozen()) } -func TestDualstakingUnbondStakeIsLowerThanMinSelfDelegationCausesUnstake(t *testing.T) { +func TestDualstakingUnbondStakeIsLowerThanMinSelfDelegationCausesError(t *testing.T) { ts := newTester(t) // 0 delegator, 1 provider staked, 0 provider unstaked, 0 provider unstaking @@ -560,16 +535,8 @@ func TestDualstakingUnbondStakeIsLowerThanMinSelfDelegationCausesUnstake(t *test amountToUnbond := staked.SubAmount(math.OneInt()) // unbond once (not unstaking completely but still below min stake) - _, err := ts.TxDualstakingUnbond(provider1Acct.GetVaultAddr(), provider, ts.spec.Name, amountToUnbond) - require.NoError(t, err) - - stakeEntry := ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) - require.True(t, staked.IsEqual(stakeEntry.Stake)) - - // advance epoch to digest the delegate - ts.AdvanceEpoch() - // provider should be unstaked -> getStakeEntry should panic - require.Panics(t, func() { ts.getStakeEntry(provider1Acct.Addr.String(), ts.spec.Name) }) + _, err := ts.TxDualstakingUnbond(provider1Acct.GetVaultAddr(), provider, amountToUnbond) + require.Error(t, err) } func TestDualstakingBondStakeIsGreaterThanMinStakeCausesUnFreeze(t *testing.T) { @@ -584,7 +551,7 @@ func TestDualstakingBondStakeIsGreaterThanMinStakeCausesUnFreeze(t *testing.T) { // delegate once amount := sdk.NewCoin("ulava", sdk.NewInt(10000)) - _, err := ts.TxDualstakingDelegate(provider1Acct.GetVaultAddr(), provider1Addr, ts.spec.Name, amount) + _, err := ts.TxDualstakingDelegate(provider1Acct.GetVaultAddr(), provider1Addr, amount) require.NoError(t, err) // advance epoch to digest the delegate @@ -609,8 +576,8 @@ func TestDualstakingRedelegateFreezeOneUnFreezeOther(t *testing.T) { // redelegate once minSelfDelegation := ts.Keepers.Dualstaking.MinSelfDelegation(ts.Ctx) - amountToUnbond := stake.Sub(stake.Sub(minSelfDelegation.AddAmount(math.OneInt()))) - _, err := ts.TxDualstakingRedelegate(provider1Acct.GetVaultAddr(), provider1Addr, provider2Addr, ts.spec.Name, ts.spec.Name, stake.Sub(amountToUnbond)) + amountToUnbond := minSelfDelegation.AddAmount(math.OneInt()) + _, err := ts.TxDualstakingRedelegate(provider1Acct.GetVaultAddr(), provider1Addr, provider2Addr, stake.Sub(amountToUnbond)) require.NoError(t, err) // advance epoch to digest the delegate @@ -627,7 +594,7 @@ func TestDualstakingRedelegateFreezeOneUnFreezeOther(t *testing.T) { require.False(t, stakeEntry.IsFrozen()) // redelegate again - _, err = ts.TxDualstakingRedelegate(provider2Acct.GetVaultAddr(), provider2Addr, provider1Addr, ts.spec.Name, ts.spec.Name, stake.SubAmount(amountToUnbond.Amount)) + _, err = ts.TxDualstakingRedelegate(provider2Acct.GetVaultAddr(), provider2Addr, provider1Addr, stake.SubAmount(amountToUnbond.Amount)) require.NoError(t, err) // advance epoch to digest the delegate diff --git a/x/dualstaking/keeper/delegator_reward.go b/x/dualstaking/keeper/delegator_reward.go index a1d428ee26..e2ec897392 100644 --- a/x/dualstaking/keeper/delegator_reward.go +++ b/x/dualstaking/keeper/delegator_reward.go @@ -4,68 +4,50 @@ import ( "strconv" "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v3/utils" - "github.com/lavanet/lava/v3/utils/lavaslices" "github.com/lavanet/lava/v3/x/dualstaking/types" - epochstoragetypes "github.com/lavanet/lava/v3/x/epochstorage/types" spectypes "github.com/lavanet/lava/v3/x/spec/types" ) // SetDelegatorReward set a specific DelegatorReward in the store from its index func (k Keeper) SetDelegatorReward(ctx sdk.Context, delegatorReward types.DelegatorReward) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.DelegatorRewardKeyPrefix)) - index := types.DelegationKey(delegatorReward.Provider, delegatorReward.Delegator, delegatorReward.ChainId) - b := k.cdc.MustMarshal(&delegatorReward) - store.Set(types.DelegatorRewardKey( - index, - ), b) + err := k.rewards.Set(ctx, types.DelegationKey(delegatorReward.Provider, delegatorReward.Delegator), delegatorReward) + if err != nil { + panic(err) + } } // GetDelegatorReward returns a DelegatorReward from its index -func (k Keeper) GetDelegatorReward( - ctx sdk.Context, - index string, -) (val types.DelegatorReward, found bool) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.DelegatorRewardKeyPrefix)) - - b := store.Get(types.DelegatorRewardKey( - index, - )) - if b == nil { - return val, false - } - - k.cdc.MustUnmarshal(b, &val) - return val, true +func (k Keeper) GetDelegatorReward(ctx sdk.Context, provider, delegator string) (val types.DelegatorReward, found bool) { + val, err := k.rewards.Get(ctx, types.DelegationKey(provider, delegator)) + return val, err == nil } // RemoveDelegatorReward removes a DelegatorReward from the store func (k Keeper) RemoveDelegatorReward( ctx sdk.Context, - index string, + provider, delegator string, ) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.DelegatorRewardKeyPrefix)) - store.Delete(types.DelegatorRewardKey( - index, - )) + err := k.rewards.Remove(ctx, types.DelegationKey(provider, delegator)) + if err != nil { + panic(err) + } } // GetAllDelegatorReward returns all DelegatorReward func (k Keeper) GetAllDelegatorReward(ctx sdk.Context) (list []types.DelegatorReward) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.DelegatorRewardKeyPrefix)) - iterator := sdk.KVStorePrefixIterator(store, []byte{}) - - defer iterator.Close() + iter, err := k.rewards.Iterate(ctx, nil) + if err != nil { + panic(err) + } - for ; iterator.Valid(); iterator.Next() { - var val types.DelegatorReward - k.cdc.MustUnmarshal(iterator.Value(), &val) - list = append(list, val) + list, err = iter.Values() + if err != nil { + panic(err) } - return + return list } // The reward for servicing a consumer is divided between the provider and its delegators. @@ -82,18 +64,19 @@ func (k Keeper) GetAllDelegatorReward(ctx sdk.Context) (list []types.DelegatorRe // CalcRewards calculates the provider reward and the total reward for delegators // providerReward = totalReward * ((effectiveDelegations*commission + providerStake) / effectiveStake) // delegatorsReward = totalReward - providerReward -func (k Keeper) CalcRewards(ctx sdk.Context, stakeEntry epochstoragetypes.StakeEntry, totalReward sdk.Coins, delegations []types.Delegation) (providerReward sdk.Coins, delegatorsReward sdk.Coins) { +func (k Keeper) CalcRewards(ctx sdk.Context, totalReward sdk.Coins, totalDelegations math.Int, selfDelegation types.Delegation, delegations []types.Delegation, commission uint64) (providerReward sdk.Coins, delegatorsReward sdk.Coins) { zeroCoins := sdk.NewCoins() + totalDelegationsWithSelf := totalDelegations.Add(selfDelegation.Amount.Amount) // Sanity check - effectiveStake != 0 - if stakeEntry.DelegateTotal.IsZero() && stakeEntry.Stake.IsZero() { + if totalDelegationsWithSelf.IsZero() { return zeroCoins, zeroCoins } - providerReward = totalReward.MulInt(stakeEntry.Stake.Amount).QuoInt(stakeEntry.TotalStake()) - if !stakeEntry.DelegateTotal.IsZero() && stakeEntry.DelegateCommission != 0 { - rawDelegatorsReward := totalReward.MulInt(stakeEntry.DelegateTotal.Amount).QuoInt(stakeEntry.TotalStake()) - providerCommission := rawDelegatorsReward.MulInt(sdk.NewIntFromUint64(stakeEntry.DelegateCommission)).QuoInt(sdk.NewInt(100)) + providerReward = totalReward.MulInt(selfDelegation.Amount.Amount).QuoInt(totalDelegationsWithSelf) + if !totalDelegations.IsZero() && commission != 0 { + rawDelegatorsReward := totalReward.MulInt(totalDelegations).QuoInt(totalDelegationsWithSelf) + providerCommission := rawDelegatorsReward.MulInt(sdk.NewIntFromUint64(commission)).QuoInt(sdk.NewInt(100)) providerReward = providerReward.Add(providerCommission...) } @@ -136,9 +119,7 @@ func (k Keeper) ClaimRewards(ctx sdk.Context, delegator string, provider string) rewardCoins := sdk.NewCoins() for _, reward := range res.Rewards { rewardCoins = rewardCoins.Add(reward.Amount...) - - ind := types.DelegationKey(reward.Provider, delegator, reward.ChainId) - k.RemoveDelegatorReward(ctx, ind) + k.RemoveDelegatorReward(ctx, reward.Provider, delegator) } // not minting new coins because they're minted when the provider @@ -158,57 +139,62 @@ func (k Keeper) ClaimRewards(ctx sdk.Context, delegator string, provider string) // RewardProvidersAndDelegators is the main function handling provider rewards with delegations // it returns the provider reward amount and updates the delegatorReward map with the reward portion for each delegator -func (k Keeper) RewardProvidersAndDelegators(ctx sdk.Context, provider string, chainID string, totalReward sdk.Coins, senderModule string, calcOnlyProvider bool, calcOnlyDelegators bool, calcOnlyContributor bool) (providerReward sdk.Coins, claimableRewards sdk.Coins, err error) { +// since this function does not actually send rewards to the providers and delegator (but only allocates rewards to be claimed) +func (k Keeper) RewardProvidersAndDelegators(ctx sdk.Context, provider string, chainID string, totalReward sdk.Coins, senderModule string, calcOnlyProvider bool, calcOnlyDelegators bool, calcOnlyContributor bool) (providerReward sdk.Coins, err error) { block := uint64(ctx.BlockHeight()) zeroCoins := sdk.NewCoins() - epoch, _, err := k.epochstorageKeeper.GetEpochStartForBlock(ctx, block) + + metadata, err := k.epochstorageKeeper.GetMetadata(ctx, provider) if err != nil { - return zeroCoins, zeroCoins, utils.LavaFormatError(types.ErrCalculatingProviderReward.Error(), err, + return zeroCoins, utils.LavaFormatError(types.ErrCalculatingProviderReward.Error(), err, utils.Attribute{Key: "block", Value: block}, ) } - stakeEntry, found := k.epochstorageKeeper.GetStakeEntry(ctx, epoch, chainID, provider) - if !found { - return zeroCoins, zeroCoins, err - } - delegations, err := k.GetProviderDelegators(ctx, provider, epoch) + delegations, err := k.GetProviderDelegators(ctx, provider) if err != nil { - return zeroCoins, zeroCoins, utils.LavaFormatError("cannot get provider's delegators", err) + return zeroCoins, utils.LavaFormatError("cannot get provider's delegators", err) } - claimableRewards = totalReward // make sure this is post boost when rewards pool is introduced contributorAddresses, contributorPart := k.specKeeper.GetContributorReward(ctx, chainID) contributorsNum := sdk.NewInt(int64(len(contributorAddresses))) + contributorReward := zeroCoins if !contributorsNum.IsZero() && contributorPart.GT(math.LegacyZeroDec()) { - contributorReward := totalReward.MulInt(contributorPart.MulInt64(spectypes.ContributorPrecision).RoundInt()).QuoInt(sdk.NewInt(spectypes.ContributorPrecision)) + contributorReward = totalReward.MulInt(contributorPart.MulInt64(spectypes.ContributorPrecision).RoundInt()).QuoInt(sdk.NewInt(spectypes.ContributorPrecision)) // make sure to round it down for the integers division contributorReward = contributorReward.QuoInt(contributorsNum).MulInt(contributorsNum) - claimableRewards = totalReward.Sub(contributorReward...) if !calcOnlyContributor { err = k.PayContributors(ctx, senderModule, contributorAddresses, contributorReward, chainID) if err != nil { - return zeroCoins, zeroCoins, err + return zeroCoins, err } } } - relevantDelegations := lavaslices.Filter(delegations, - func(d types.Delegation) bool { - return d.ChainID == chainID && d.IsFirstWeekPassed(ctx.BlockTime().UTC().Unix()) && d.Delegator != stakeEntry.Vault - }) + relevantDelegations := []types.Delegation{} + totalDelegations := sdk.ZeroInt() + var selfdelegation types.Delegation + // fetch relevant delegations (those who are passed the first week of delegation), self delegation and sum the total delegations + for _, d := range delegations { + if d.Delegator == metadata.Vault { + selfdelegation = d + } else if d.IsFirstWeekPassed(ctx.BlockTime().UTC().Unix()) { + relevantDelegations = append(relevantDelegations, d) + totalDelegations = totalDelegations.Add(d.Amount.Amount) + } + } - providerReward, delegatorsReward := k.CalcRewards(ctx, stakeEntry, claimableRewards, relevantDelegations) + providerReward, delegatorsReward := k.CalcRewards(ctx, totalReward.Sub(contributorReward...), totalDelegations, selfdelegation, relevantDelegations, metadata.DelegateCommission) - leftoverRewards := k.updateDelegatorsReward(ctx, stakeEntry.DelegateTotal.Amount, relevantDelegations, delegatorsReward, senderModule, calcOnlyDelegators) + leftoverRewards := k.updateDelegatorsReward(ctx, totalDelegations, relevantDelegations, delegatorsReward, senderModule, calcOnlyDelegators) fullProviderReward := providerReward.Add(leftoverRewards...) if !calcOnlyProvider { // reward provider's vault - k.rewardDelegator(ctx, types.Delegation{Provider: stakeEntry.Address, ChainID: chainID, Delegator: stakeEntry.Vault}, fullProviderReward, senderModule) + k.rewardDelegator(ctx, types.Delegation{Provider: metadata.Provider, Delegator: metadata.Vault}, fullProviderReward, senderModule) } - return fullProviderReward, claimableRewards, nil + return fullProviderReward, nil } // updateDelegatorsReward updates the delegator rewards map @@ -217,11 +203,9 @@ func (k Keeper) updateDelegatorsReward(ctx sdk.Context, totalDelegations math.In for _, delegation := range delegations { delegatorReward := k.CalcDelegatorReward(ctx, delegatorsReward, totalDelegations, delegation) - if !calcOnly { k.rewardDelegator(ctx, delegation, delegatorReward, senderModule) } - usedDelegatorRewards = usedDelegatorRewards.Add(delegatorReward...) } @@ -233,12 +217,10 @@ func (k Keeper) rewardDelegator(ctx sdk.Context, delegation types.Delegation, am return } - rewardMapKey := types.DelegationKey(delegation.Provider, delegation.Delegator, delegation.ChainID) - delegatorReward, found := k.GetDelegatorReward(ctx, rewardMapKey) + delegatorReward, found := k.GetDelegatorReward(ctx, delegation.Provider, delegation.Delegator) if !found { delegatorReward.Provider = delegation.Provider delegatorReward.Delegator = delegation.Delegator - delegatorReward.ChainId = delegation.ChainID delegatorReward.Amount = amount } else { delegatorReward.Amount = delegatorReward.Amount.Add(amount...) diff --git a/x/dualstaking/keeper/delegator_reward_test.go b/x/dualstaking/keeper/delegator_reward_test.go index ca684a3f60..f5d2a3237b 100644 --- a/x/dualstaking/keeper/delegator_reward_test.go +++ b/x/dualstaking/keeper/delegator_reward_test.go @@ -20,7 +20,6 @@ func createNDelegatorReward(keeper *keeper.Keeper, ctx sdk.Context, n int) []typ for i := range items { items[i].Provider = "p" + strconv.Itoa(i) items[i].Delegator = "d" + strconv.Itoa(i) - items[i].ChainId = "c" + strconv.Itoa(i) keeper.SetDelegatorReward(ctx, items[i]) } return items @@ -30,10 +29,8 @@ func TestDelegatorRewardGet(t *testing.T) { keeper, ctx := keepertest.DualstakingKeeper(t) items := createNDelegatorReward(keeper, ctx, 10) for _, item := range items { - index := types.DelegationKey(item.Provider, item.Delegator, item.ChainId) rst, found := keeper.GetDelegatorReward(ctx, - index, - ) + item.Provider, item.Delegator) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -46,13 +43,10 @@ func TestDelegatorRewardRemove(t *testing.T) { keeper, ctx := keepertest.DualstakingKeeper(t) items := createNDelegatorReward(keeper, ctx, 10) for _, item := range items { - index := types.DelegationKey(item.Provider, item.Delegator, item.ChainId) keeper.RemoveDelegatorReward(ctx, - index, - ) + item.Provider, item.Delegator) _, found := keeper.GetDelegatorReward(ctx, - index, - ) + item.Provider, item.Delegator) require.False(t, found) } } diff --git a/x/dualstaking/keeper/grpc_query_delegator_providers.go b/x/dualstaking/keeper/grpc_query_delegator_providers.go index 172a6e6d65..f94530c7d2 100644 --- a/x/dualstaking/keeper/grpc_query_delegator_providers.go +++ b/x/dualstaking/keeper/grpc_query_delegator_providers.go @@ -2,10 +2,8 @@ package keeper import ( "context" - "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/lavanet/lava/v3/utils" "github.com/lavanet/lava/v3/x/dualstaking/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -18,30 +16,15 @@ func (k Keeper) DelegatorProviders(goCtx context.Context, req *types.QueryDelega ctx := sdk.UnwrapSDKContext(goCtx) - epoch := uint64(ctx.BlockHeight()) - if req.WithPending { - epoch = k.epochstorageKeeper.GetCurrentNextEpoch(ctx) - } - - providers, err := k.GetDelegatorProviders(ctx, req.Delegator, epoch) + providers, err := k.GetDelegatorProviders(ctx, req.Delegator) if err != nil { return nil, err } var delegations []types.Delegation for _, provider := range providers { - indices := k.delegationFS.GetAllEntryIndicesWithPrefix(ctx, types.DelegationKey(provider, req.Delegator, "")) - for _, ind := range indices { - var delegation types.Delegation - found := k.delegationFS.FindEntry(ctx, ind, epoch, &delegation) - if !found { - utils.LavaFormatError("critical: provider found in delegatorFS but not in delegationFS", fmt.Errorf("provider delegation not found"), - utils.Attribute{Key: "delegator", Value: req.Delegator}, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: delegation.ChainID}, - ) - continue - } + delegation, found := k.GetDelegation(ctx, provider, req.Delegator) + if found { delegations = append(delegations, delegation) } } diff --git a/x/dualstaking/keeper/grpc_query_delegator_providers_test.go b/x/dualstaking/keeper/grpc_query_delegator_providers_test.go index a8688e6eee..e546fa206c 100644 --- a/x/dualstaking/keeper/grpc_query_delegator_providers_test.go +++ b/x/dualstaking/keeper/grpc_query_delegator_providers_test.go @@ -17,49 +17,47 @@ func TestQueryWithUnbonding(t *testing.T) { _, delegator := ts.GetAccount(common.CONSUMER, 0) _, provider := ts.GetAccount(common.PROVIDER, 0) - spec := ts.Spec("mock") - amountUint64 := uint64(100) amount := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(amountUint64)) // delegate and query - _, err := ts.TxDualstakingDelegate(delegator, provider, spec.Index, amount) + _, err := ts.TxDualstakingDelegate(delegator, provider, amount) require.NoError(t, err) ts.AdvanceEpoch() - delegation := types.NewDelegation(delegator, provider, spec.Index, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation := types.NewDelegation(delegator, provider, ts.Ctx.BlockTime(), ts.TokenDenom()) delegation.Amount = amount - res, err := ts.QueryDualstakingDelegatorProviders(delegator, false) + res, err := ts.QueryDualstakingDelegatorProviders(delegator) require.NoError(t, err) delegationRes := res.Delegations[0] require.True(t, delegation.Equal(&delegationRes)) // partially unbond and query unbondAmount := amount.Sub(sdk.NewCoin(ts.TokenDenom(), sdk.OneInt())) - _, err = ts.TxDualstakingUnbond(delegator, provider, spec.Index, unbondAmount) + _, err = ts.TxDualstakingUnbond(delegator, provider, unbondAmount) require.NoError(t, err) ts.AdvanceEpoch() bondedAmount := amount.Sub(unbondAmount) delegation.Amount = bondedAmount - res, err = ts.QueryDualstakingDelegatorProviders(delegator, false) + res, err = ts.QueryDualstakingDelegatorProviders(delegator) require.NoError(t, err) delegationRes = res.Delegations[0] require.True(t, delegation.Equal(&delegationRes)) // unbond completely and query (should not get providers) - _, err = ts.TxDualstakingUnbond(delegator, provider, spec.Index, bondedAmount) + _, err = ts.TxDualstakingUnbond(delegator, provider, bondedAmount) require.NoError(t, err) ts.AdvanceEpoch() - res, err = ts.QueryDualstakingDelegatorProviders(delegator, false) + res, err = ts.QueryDualstakingDelegatorProviders(delegator) require.NoError(t, err) require.Equal(t, 0, len(res.Delegations)) } -func TestQueryWithPendingDelegations(t *testing.T) { +func TestQueryDelegations(t *testing.T) { ts := newTester(t) ts.setupForDelegation(2, 1, 0, 0) // 2 delegators, 1 staked provider @@ -68,62 +66,45 @@ func TestQueryWithPendingDelegations(t *testing.T) { _, delegator2 := ts.GetAccount(common.CONSUMER, 1) _, provider := ts.GetAccount(common.PROVIDER, 0) - spec := ts.Spec("mock") - amountUint64 := uint64(100) amount := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(amountUint64)) - delegation1 := types.NewDelegation(delegator1, provider, spec.Index, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation1 := types.NewDelegation(delegator1, provider, ts.Ctx.BlockTime(), ts.TokenDenom()) delegation1.Amount = amount // delegate without advancing an epoch - _, err := ts.TxDualstakingDelegate(delegator1, provider, spec.Index, amount) + _, err := ts.TxDualstakingDelegate(delegator1, provider, amount) require.NoError(t, err) // query pending delegators - res, err := ts.QueryDualstakingDelegatorProviders(delegator1, true) + res, err := ts.QueryDualstakingDelegatorProviders(delegator1) require.NoError(t, err) require.Equal(t, 1, len(res.Delegations)) delegationRes := res.Delegations[0] require.True(t, delegationRes.Equal(&delegation1)) - // query current delegators - res, err = ts.QueryDualstakingDelegatorProviders(delegator1, false) - require.NoError(t, err) - require.Equal(t, 0, len(res.Delegations)) - // advance epoch, delegator1 should show in both flag values ts.AdvanceEpoch() - res, err = ts.QueryDualstakingDelegatorProviders(delegator1, true) - require.NoError(t, err) - require.Equal(t, 1, len(res.Delegations)) - delegationRes = res.Delegations[0] - require.True(t, delegationRes.Equal(&delegation1)) - - res, err = ts.QueryDualstakingDelegatorProviders(delegator1, false) + res, err = ts.QueryDualstakingDelegatorProviders(delegator1) require.NoError(t, err) require.Equal(t, 1, len(res.Delegations)) delegationRes = res.Delegations[0] require.True(t, delegationRes.Equal(&delegation1)) // delegate delegator2 and query again - delegation2 := types.NewDelegation(delegator2, provider, spec.Index, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation2 := types.NewDelegation(delegator2, provider, ts.Ctx.BlockTime(), ts.TokenDenom()) delegation2.Amount = amount - _, err = ts.TxDualstakingDelegate(delegator2, provider, spec.Index, amount) + _, err = ts.TxDualstakingDelegate(delegator2, provider, amount) require.NoError(t, err) // delegator2 should show when quering with showPending=true and not show when showPending=false - res, err = ts.QueryDualstakingDelegatorProviders(delegator2, true) + res, err = ts.QueryDualstakingDelegatorProviders(delegator2) require.NoError(t, err) require.Equal(t, 1, len(res.Delegations)) delegationRes = res.Delegations[0] require.True(t, delegationRes.Equal(&delegation2)) - - res, err = ts.QueryDualstakingDelegatorProviders(delegator2, false) - require.NoError(t, err) - require.Equal(t, 0, len(res.Delegations)) } func TestQueryProviderMultipleDelegators(t *testing.T) { @@ -140,7 +121,6 @@ func TestQueryProviderMultipleDelegators(t *testing.T) { delegators := []string{delegator1, delegator2, delegator3} - spec := ts.Spec("mock") spec1 := ts.Spec("mock1") err := ts.StakeProvider(providerAcc.GetVaultAddr(), provider, spec1, testStake) require.NoError(t, err) @@ -150,16 +130,10 @@ func TestQueryProviderMultipleDelegators(t *testing.T) { delegations := []types.Delegation{} for i := 0; i < len(delegators); i++ { - var chainID string - if i == 0 { - chainID = spec.Index - } else { - chainID = spec1.Index - } - _, err := ts.TxDualstakingDelegate(delegators[i], provider, chainID, amount) + _, err := ts.TxDualstakingDelegate(delegators[i], provider, amount) require.NoError(t, err) - delegation := types.NewDelegation(delegators[i], provider, chainID, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation := types.NewDelegation(delegators[i], provider, ts.Ctx.BlockTime(), ts.TokenDenom()) delegation.Amount = amount delegations = append(delegations, delegation) } @@ -167,7 +141,7 @@ func TestQueryProviderMultipleDelegators(t *testing.T) { ts.AdvanceEpoch() for i := 0; i < len(delegations); i++ { - res, err := ts.QueryDualstakingDelegatorProviders(delegations[i].Delegator, false) + res, err := ts.QueryDualstakingDelegatorProviders(delegations[i].Delegator) require.NoError(t, err) require.Equal(t, 1, len(res.Delegations)) resDelegation := res.Delegations[0] @@ -187,29 +161,28 @@ func TestQueryDelegatorMultipleProviders(t *testing.T) { providers := []string{provider1, provider2, provider3} - spec := ts.Spec("mock") - amountUint64 := uint64(100) amount := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(amountUint64)) - delegations := []types.Delegation{} + delegations := map[string]types.Delegation{} for i := 0; i < len(providers); i++ { - _, err := ts.TxDualstakingDelegate(delegator, providers[i], spec.Index, amount) + _, err := ts.TxDualstakingDelegate(delegator, providers[i], amount) require.NoError(t, err) - delegation := types.NewDelegation(delegator, providers[i], spec.Index, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation := types.NewDelegation(delegator, providers[i], ts.Ctx.BlockTime(), ts.TokenDenom()) delegation.Amount = amount - delegations = append(delegations, delegation) + delegations[providers[i]] = delegation } ts.AdvanceEpoch() - res, err := ts.QueryDualstakingDelegatorProviders(delegator, false) + res, err := ts.QueryDualstakingDelegatorProviders(delegator) require.NoError(t, err) require.Equal(t, 3, len(res.Delegations)) for i := 0; i < len(delegations); i++ { resDelegation := res.Delegations[i] - require.True(t, resDelegation.Equal(&delegations[i])) + delegation := delegations[resDelegation.Provider] + require.True(t, resDelegation.Equal(&delegation)) } } @@ -222,16 +195,14 @@ func TestQueryDelegatorUnstakedProvider(t *testing.T) { _, unstakedProvider := ts.GetAccount(common.PROVIDER, 0) _, unstakingProvider := ts.GetAccount(common.PROVIDER, 1) - spec := ts.Spec("mock") - amountUint64 := uint64(100) amount := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(amountUint64)) // shouldn't be able to delegate to unstaked provider - _, err := ts.TxDualstakingDelegate(delegator, unstakedProvider, spec.Index, amount) + _, err := ts.TxDualstakingDelegate(delegator, unstakedProvider, amount) require.Error(t, err) // shouldn't be able to delegate to unstaking provider (even though it didn't get its funds back, it's still considered unstaked) - _, err = ts.TxDualstakingDelegate(delegator, unstakingProvider, spec.Index, amount) + _, err = ts.TxDualstakingDelegate(delegator, unstakingProvider, amount) require.Error(t, err) } diff --git a/x/dualstaking/keeper/grpc_query_delegator_rewards.go b/x/dualstaking/keeper/grpc_query_delegator_rewards.go index bea3ab4281..36dcd1a84b 100644 --- a/x/dualstaking/keeper/grpc_query_delegator_rewards.go +++ b/x/dualstaking/keeper/grpc_query_delegator_rewards.go @@ -23,14 +23,11 @@ func (k Keeper) DelegatorRewards(goCtx context.Context, req *types.QueryDelegato } for _, delegation := range resProviders.Delegations { - if (delegation.ChainID == req.ChainId || req.ChainId == "") && - (delegation.Provider == req.Provider || req.Provider == "") { - ind := types.DelegationKey(delegation.Provider, req.Delegator, delegation.ChainID) - delegatorReward, found := k.GetDelegatorReward(ctx, ind) + if delegation.Provider == req.Provider || req.Provider == "" { + delegatorReward, found := k.GetDelegatorReward(ctx, delegation.Provider, delegation.Delegator) if found { reward := types.DelegatorRewardInfo{ Provider: delegation.Provider, - ChainId: delegation.ChainID, Amount: delegatorReward.Amount, } rewards = append(rewards, reward) diff --git a/x/dualstaking/keeper/grpc_query_provider_delegators.go b/x/dualstaking/keeper/grpc_query_provider_delegators.go index 72085ad045..6d02347ac7 100644 --- a/x/dualstaking/keeper/grpc_query_provider_delegators.go +++ b/x/dualstaking/keeper/grpc_query_provider_delegators.go @@ -16,12 +16,7 @@ func (k Keeper) ProviderDelegators(goCtx context.Context, req *types.QueryProvid ctx := sdk.UnwrapSDKContext(goCtx) - epoch := uint64(ctx.BlockHeight()) - if req.WithPending { - epoch = k.epochstorageKeeper.GetCurrentNextEpoch(ctx) - } - - delegations, err := k.GetProviderDelegators(ctx, req.Provider, epoch) + delegations, err := k.GetProviderDelegators(ctx, req.Provider) if err != nil { return nil, err } diff --git a/x/dualstaking/keeper/grpc_query_provider_delegators_test.go b/x/dualstaking/keeper/grpc_query_provider_delegators_test.go index 4ae641a48e..e60d13ef6f 100644 --- a/x/dualstaking/keeper/grpc_query_provider_delegators_test.go +++ b/x/dualstaking/keeper/grpc_query_provider_delegators_test.go @@ -17,20 +17,18 @@ func TestQueryProviderDelegatorsWithUnbonding(t *testing.T) { _, delegator := ts.GetAccount(common.CONSUMER, 0) _, provider := ts.GetAccount(common.PROVIDER, 0) - spec := ts.Spec("mock") - amountUint64 := uint64(100) amount := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(amountUint64)) // delegate and query - _, err := ts.TxDualstakingDelegate(delegator, provider, spec.Index, amount) + _, err := ts.TxDualstakingDelegate(delegator, provider, amount) require.NoError(t, err) ts.AdvanceEpoch() - delegation := types.NewDelegation(delegator, provider, spec.Index, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation := types.NewDelegation(delegator, provider, ts.Ctx.BlockTime(), ts.TokenDenom()) delegation.Amount = amount - res, err := ts.QueryDualstakingProviderDelegators(provider, false) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) var delegationRes types.Delegation @@ -43,14 +41,14 @@ func TestQueryProviderDelegatorsWithUnbonding(t *testing.T) { // partially unbond and query unbondAmount := amount.Sub(sdk.NewCoin(ts.TokenDenom(), sdk.OneInt())) - _, err = ts.TxDualstakingUnbond(delegator, provider, spec.Index, unbondAmount) + _, err = ts.TxDualstakingUnbond(delegator, provider, unbondAmount) require.NoError(t, err) ts.AdvanceEpoch() bondedAmount := amount.Sub(unbondAmount) delegation.Amount = bondedAmount - res, err = ts.QueryDualstakingProviderDelegators(provider, false) + res, err = ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) for _, d := range res.Delegations { @@ -61,16 +59,16 @@ func TestQueryProviderDelegatorsWithUnbonding(t *testing.T) { require.True(t, delegation.Equal(&delegationRes)) // unbond completely and query (should not get providers) - _, err = ts.TxDualstakingUnbond(delegator, provider, spec.Index, bondedAmount) + _, err = ts.TxDualstakingUnbond(delegator, provider, bondedAmount) require.NoError(t, err) ts.AdvanceEpoch() - res, err = ts.QueryDualstakingProviderDelegators(provider, false) + res, err = ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 1, len(res.Delegations)) } -func TestQueryProviderDelegatorsWithPendingDelegations(t *testing.T) { +func TestQueryProviderDelegators(t *testing.T) { ts := newTester(t) ts.setupForDelegation(2, 1, 0, 0) // 2 delegators, 1 staked provider @@ -79,20 +77,18 @@ func TestQueryProviderDelegatorsWithPendingDelegations(t *testing.T) { _, delegator2 := ts.GetAccount(common.CONSUMER, 1) _, provider := ts.GetAccount(common.PROVIDER, 0) - spec := ts.Spec("mock") - amountUint64 := uint64(100) amount := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(amountUint64)) - delegation1 := types.NewDelegation(delegator1, provider, spec.Index, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation1 := types.NewDelegation(delegator1, provider, ts.Ctx.BlockTime(), ts.TokenDenom()) delegation1.Amount = amount // delegate without advancing an epoch - _, err := ts.TxDualstakingDelegate(delegator1, provider, spec.Index, amount) + _, err := ts.TxDualstakingDelegate(delegator1, provider, amount) require.NoError(t, err) // query pending delegators - res, err := ts.QueryDualstakingProviderDelegators(provider, true) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) var delegationRes types.Delegation @@ -103,15 +99,7 @@ func TestQueryProviderDelegatorsWithPendingDelegations(t *testing.T) { } require.True(t, delegationRes.Equal(&delegation1)) - // query current delegators - res, err = ts.QueryDualstakingProviderDelegators(provider, false) - require.NoError(t, err) - require.Equal(t, 1, len(res.Delegations)) - - // advance epoch, delegator1 should show in both flag values - ts.AdvanceEpoch() - - res, err = ts.QueryDualstakingProviderDelegators(provider, true) + res, err = ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) for _, d := range res.Delegations { @@ -121,7 +109,7 @@ func TestQueryProviderDelegatorsWithPendingDelegations(t *testing.T) { } require.True(t, delegationRes.Equal(&delegation1)) - res, err = ts.QueryDualstakingProviderDelegators(provider, false) + res, err = ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) for _, d := range res.Delegations { @@ -132,13 +120,13 @@ func TestQueryProviderDelegatorsWithPendingDelegations(t *testing.T) { require.True(t, delegationRes.Equal(&delegation1)) // delegate delegator2 and query again - delegation2 := types.NewDelegation(delegator2, provider, spec.Index, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation2 := types.NewDelegation(delegator2, provider, ts.Ctx.BlockTime(), ts.TokenDenom()) delegation2.Amount = amount - _, err = ts.TxDualstakingDelegate(delegator2, provider, spec.Index, amount) + _, err = ts.TxDualstakingDelegate(delegator2, provider, amount) require.NoError(t, err) // delegator2 should show when quering with showPending=true and not show when showPending=false - res, err = ts.QueryDualstakingProviderDelegators(provider, true) + res, err = ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 3, len(res.Delegations)) for _, d := range res.Delegations { @@ -148,16 +136,6 @@ func TestQueryProviderDelegatorsWithPendingDelegations(t *testing.T) { require.True(t, d.Equal(&delegation2)) } } - - res, err = ts.QueryDualstakingProviderDelegators(provider, false) - require.NoError(t, err) - require.Equal(t, 2, len(res.Delegations)) - for _, d := range res.Delegations { - if d.Delegator == delegation1.Delegator { - delegationRes = d - } - } - require.True(t, delegationRes.Equal(&delegation1)) } func TestQueryProviderDelegatorsProviderMultipleDelegators(t *testing.T) { @@ -174,7 +152,6 @@ func TestQueryProviderDelegatorsProviderMultipleDelegators(t *testing.T) { delegators := []string{delegator1, delegator2, delegator3} - spec := ts.Spec("mock") spec1 := ts.Spec("mock1") err := ts.StakeProvider(providerAcc.GetVaultAddr(), provider, spec1, testStake) require.NoError(t, err) @@ -184,22 +161,16 @@ func TestQueryProviderDelegatorsProviderMultipleDelegators(t *testing.T) { delegations := []types.Delegation{} for i := 0; i < len(delegators); i++ { - var chainID string - if i == 0 { - chainID = spec.Index - } else { - chainID = spec1.Index - } - _, err := ts.TxDualstakingDelegate(delegators[i], provider, chainID, amount) + _, err := ts.TxDualstakingDelegate(delegators[i], provider, amount) require.NoError(t, err) - delegation := types.NewDelegation(delegators[i], provider, chainID, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation := types.NewDelegation(delegators[i], provider, ts.Ctx.BlockTime(), ts.TokenDenom()) delegation.Amount = amount delegations = append(delegations, delegation) } ts.AdvanceEpoch() - res, err := ts.QueryDualstakingProviderDelegators(provider, false) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.Equal(t, 4, len(res.Delegations)) require.NoError(t, err) for _, d := range delegations { @@ -223,17 +194,15 @@ func TestQueryProviderDelegatorsDelegatorMultipleProviders(t *testing.T) { providers := []string{provider1, provider2, provider3} - spec := ts.Spec("mock") - amountUint64 := uint64(100) amount := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(amountUint64)) delegations := []types.Delegation{} for i := 0; i < len(providers); i++ { - _, err := ts.TxDualstakingDelegate(delegator, providers[i], spec.Index, amount) + _, err := ts.TxDualstakingDelegate(delegator, providers[i], amount) require.NoError(t, err) - delegation := types.NewDelegation(delegator, providers[i], spec.Index, ts.Ctx.BlockTime(), ts.TokenDenom()) + delegation := types.NewDelegation(delegator, providers[i], ts.Ctx.BlockTime(), ts.TokenDenom()) delegation.Amount = amount delegations = append(delegations, delegation) } @@ -241,7 +210,7 @@ func TestQueryProviderDelegatorsDelegatorMultipleProviders(t *testing.T) { ts.AdvanceEpoch() for i := 0; i < len(providers); i++ { - res, err := ts.QueryDualstakingProviderDelegators(providers[i], false) + res, err := ts.QueryDualstakingProviderDelegators(providers[i]) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) var delegationRes types.Delegation @@ -263,16 +232,14 @@ func TestQueryProviderDelegatorsDelegatorUnstakedProvider(t *testing.T) { _, unstakedProvider := ts.GetAccount(common.PROVIDER, 0) _, unstakingProvider := ts.GetAccount(common.PROVIDER, 1) - spec := ts.Spec("mock") - amountUint64 := uint64(100) amount := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(amountUint64)) // shouldn't be able to delegate to unstaked provider - _, err := ts.TxDualstakingDelegate(delegator, unstakedProvider, spec.Index, amount) + _, err := ts.TxDualstakingDelegate(delegator, unstakedProvider, amount) require.Error(t, err) // shouldn't be able to delegate to unstaking provider (even though it didn't get its funds back, it's still considered unstaked) - _, err = ts.TxDualstakingDelegate(delegator, unstakingProvider, spec.Index, amount) + _, err = ts.TxDualstakingDelegate(delegator, unstakingProvider, amount) require.Error(t, err) } diff --git a/x/dualstaking/keeper/hooks_test.go b/x/dualstaking/keeper/hooks_test.go index 9a46c24953..f988230a56 100644 --- a/x/dualstaking/keeper/hooks_test.go +++ b/x/dualstaking/keeper/hooks_test.go @@ -28,7 +28,7 @@ func TestCreateValidator(t *testing.T) { amount := sdk.NewIntFromUint64(100) ts.TxCreateValidator(validator, amount) - res, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER, true) + res, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER) require.NoError(t, err) require.Equal(t, res.Delegations[0].Delegator, validator.Addr.String()) } @@ -43,7 +43,7 @@ func TestDelegateToValidator(t *testing.T) { amount := sdk.NewIntFromUint64(100) ts.TxCreateValidator(validator, amount) - res, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER, true) + res, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER) require.NoError(t, err) require.Equal(t, res.Delegations[0].Delegator, validator.Addr.String()) @@ -52,7 +52,7 @@ func TestDelegateToValidator(t *testing.T) { _, err = ts.TxDelegateValidator(delegator, validator, amount) require.NoError(t, err) - res, err = ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER, true) + res, err = ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) require.True(t, res.Delegations[0].Delegator == validator.Addr.String() || res.Delegations[0].Delegator == delegator.Addr.String()) @@ -71,7 +71,7 @@ func TestReDelegateToValidator(t *testing.T) { ts.TxCreateValidator(validator1, amount) ts.TxCreateValidator(validator2, amount) - delegatorsRes, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER, true) + delegatorsRes, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER) require.NoError(t, err) require.Equal(t, 2, len(delegatorsRes.Delegations)) @@ -80,11 +80,11 @@ func TestReDelegateToValidator(t *testing.T) { _, err = ts.TxDelegateValidator(delegator, validator1, amount) require.NoError(t, err) - delegatorsRes, err = ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER, true) + delegatorsRes, err = ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER) require.NoError(t, err) require.Equal(t, 3, len(delegatorsRes.Delegations)) - providersRes, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String(), true) + providersRes, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String()) require.NoError(t, err) require.Equal(t, 1, len(providersRes.Delegations)) require.Equal(t, delegator.Addr.String(), providersRes.Delegations[0].Delegator) @@ -92,11 +92,11 @@ func TestReDelegateToValidator(t *testing.T) { _, err = ts.TxReDelegateValidator(delegator, validator1, validator2, amount) require.NoError(t, err) - delegatorsRes1, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER, true) + delegatorsRes1, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER) require.NoError(t, err) require.Equal(t, delegatorsRes, delegatorsRes1) - providersRes1, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String(), true) + providersRes1, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String()) require.NoError(t, err) require.Equal(t, providersRes, providersRes1) } @@ -128,7 +128,7 @@ func TestReDelegateToProvider(t *testing.T) { require.True(t, found) require.Equal(t, amount, entry.Stake.Amount) - providersRes, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String(), true) + providersRes, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String()) require.NoError(t, err) require.Equal(t, 1, len(providersRes.Delegations)) require.Equal(t, commontypes.EMPTY_PROVIDER, providersRes.Delegations[0].Provider) @@ -138,19 +138,12 @@ func TestReDelegateToProvider(t *testing.T) { _, err = ts.TxDualstakingRedelegate(delegator.Addr.String(), commontypes.EMPTY_PROVIDER, provider, - commontypes.EMPTY_PROVIDER_CHAINID, - entry.Chain, sdk.NewCoin(ts.TokenDenom(), amount)) require.NoError(t, err) - providersRes1, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String(), false) + providersRes1, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String()) require.NoError(t, err) - require.Equal(t, providersRes, providersRes1) - - providersRes1, err = ts.QueryDualstakingDelegatorProviders(delegator.Addr.String(), true) - require.NoError(t, err) - require.Equal(t, provider, providersRes1.Delegations[0].Provider) ts.AdvanceEpoch() @@ -159,6 +152,7 @@ func TestReDelegateToProvider(t *testing.T) { require.True(t, found) require.Equal(t, amount, entry.DelegateTotal.Amount) require.Equal(t, amount, entry.Stake.Amount) + require.Equal(t, provider, providersRes1.Delegations[0].Provider) } // TestUnbondUniformProviders checks that the uniform unbond of providers (that is triggered by a validator unbond) @@ -208,8 +202,6 @@ func TestUnbondUniformProviders(t *testing.T) { _, err = ts.TxDualstakingRedelegate(delegatorAcc.Addr.String(), commontypes.EMPTY_PROVIDER, provider, - commontypes.EMPTY_PROVIDER_CHAINID, - ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), redelegateAmts[i])) require.NoError(t, err) } @@ -218,7 +210,7 @@ func TestUnbondUniformProviders(t *testing.T) { _, err = ts.TxUnbondValidator(delegatorAcc, validator, sdk.NewInt(25*5)) require.NoError(t, err) - res, err := ts.QueryDualstakingDelegatorProviders(delegator, true) + res, err := ts.QueryDualstakingDelegatorProviders(delegator) require.Len(t, res.Delegations, 3) require.NoError(t, err) for _, d := range res.Delegations { @@ -254,7 +246,7 @@ func TestValidatorSlash(t *testing.T) { require.Equal(t, amount, val.Tokens) // sanity check: empty provider should have delegation of 1000000000 tokens - resQ, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER, false) + resQ, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER) require.NoError(t, err) require.Equal(t, 1, len(resQ.Delegations)) require.Equal(t, amount, resQ.Delegations[0].Amount.Amount) @@ -268,7 +260,7 @@ func TestValidatorSlash(t *testing.T) { // check: the only delegation should be validator delegated to empty provider // the delegation amount should be original_amount(=1000000000) - expectedTokensToBurn - res, err := ts.QueryDualstakingDelegatorProviders(valAcc.Addr.String(), true) + res, err := ts.QueryDualstakingDelegatorProviders(valAcc.Addr.String()) require.NoError(t, err) require.Equal(t, 1, len(res.Delegations)) // empty provider require.Equal(t, amount.Sub(expectedTokensToBurn), res.Delegations[0].Amount.Amount) @@ -322,7 +314,7 @@ func TestValidatorAndProvidersSlash(t *testing.T) { ts.AdvanceEpoch() // advance epoch to apply the empty provider delegation (that happens automatically when delegating to the validator) // sanity check: empty provider should have val_stake(=1000000000) + 250*consensusPowerTokens tokens in two delegations - resQ, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER, false) + resQ, err := ts.QueryDualstakingProviderDelegators(commontypes.EMPTY_PROVIDER) require.NoError(t, err) require.Equal(t, 2, len(resQ.Delegations)) require.Equal(t, stake.Add(consensusPowerTokens.MulRaw(250)), resQ.Delegations[0].Amount.Amount.Add(resQ.Delegations[1].Amount.Amount)) @@ -343,14 +335,12 @@ func TestValidatorAndProvidersSlash(t *testing.T) { _, err = ts.TxDualstakingRedelegate(delegatorAcc.Addr.String(), commontypes.EMPTY_PROVIDER, provider, - commontypes.EMPTY_PROVIDER_CHAINID, - ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), redelegateAmts[i])) require.NoError(t, err) ts.AdvanceEpoch() // verify delegation is applied (should be 2 delegations: self delegation + redelegate amount) - resQ, err = ts.QueryDualstakingProviderDelegators(provider, false) + resQ, err = ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(resQ.Delegations)) require.Equal(t, redelegateAmts[i].Add(stake), resQ.Delegations[0].Amount.Amount.Add(resQ.Delegations[1].Amount.Amount)) @@ -361,7 +351,7 @@ func TestValidatorAndProvidersSlash(t *testing.T) { ts.AdvanceBlockUntilStale() // sanity check: redelegate from provider0 to provider1 and check delegations balance - _, err = ts.TxDualstakingRedelegate(delegator, providers[0], providers[1], ts.spec.Index, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), consensusPowerTokens.MulRaw(5))) + _, err = ts.TxDualstakingRedelegate(delegator, providers[0], providers[1], sdk.NewCoin(ts.TokenDenom(), consensusPowerTokens.MulRaw(5))) require.NoError(t, err) ts.AdvanceEpoch() // apply redelegation diff, _, err := ts.Keepers.Dualstaking.VerifyDelegatorBalance(ts.Ctx, delegatorAcc.Addr) @@ -369,7 +359,7 @@ func TestValidatorAndProvidersSlash(t *testing.T) { require.True(t, diff.IsZero()) // sanity check: unbond some of provider2's funds and check delegations balance - _, err = ts.TxDualstakingUnbond(delegator, providers[2], ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), consensusPowerTokens.MulRaw(5))) + _, err = ts.TxDualstakingUnbond(delegator, providers[2], sdk.NewCoin(ts.TokenDenom(), consensusPowerTokens.MulRaw(5))) require.NoError(t, err) ts.AdvanceEpoch() // apply unbond diff, _, err = ts.Keepers.Dualstaking.VerifyDelegatorBalance(ts.Ctx, delegatorAcc.Addr) @@ -378,7 +368,7 @@ func TestValidatorAndProvidersSlash(t *testing.T) { expectedValidatorTokens = expectedValidatorTokens.Sub(consensusPowerTokens.MulRaw(5)) // get the delegator's provider delegations before the slash - res, err := ts.QueryDualstakingDelegatorProviders(delegator, true) + res, err := ts.QueryDualstakingDelegatorProviders(delegator) require.NoError(t, err) delegationsBeforeSlash := res.Delegations slices.SortFunc(delegationsBeforeSlash, func(i, j dualstakingtypes.Delegation) bool { @@ -396,13 +386,13 @@ func TestValidatorAndProvidersSlash(t *testing.T) { // both the validator and providers have a single delegation that was created by their // self delegation. Check that the new amount after slash is (1-fraction) * old_amount - res, err = ts.QueryDualstakingDelegatorProviders(valAcc.Addr.String(), true) + res, err = ts.QueryDualstakingDelegatorProviders(valAcc.Addr.String()) require.NoError(t, err) require.Len(t, res.Delegations, 1) require.Equal(t, sdk.OneDec().Sub(fraction).MulInt(stake).RoundInt(), res.Delegations[0].Amount.Amount) for _, p := range providersAccs { - res, err = ts.QueryDualstakingDelegatorProviders(p.GetVaultAddr(), true) + res, err = ts.QueryDualstakingDelegatorProviders(p.GetVaultAddr()) require.NoError(t, err) require.Len(t, res.Delegations, 1) require.Equal(t, sdk.OneDec().Sub(fraction).MulInt(stake).RoundInt(), res.Delegations[0].Amount.Amount) @@ -410,7 +400,7 @@ func TestValidatorAndProvidersSlash(t *testing.T) { // the total token to deduct from the delegator's provider delegations is: // total_providers_delegations * fraction = (245 * consensus_power_tokens) * fraction - res, err = ts.QueryDualstakingDelegatorProviders(delegator, true) + res, err = ts.QueryDualstakingDelegatorProviders(delegator) require.NoError(t, err) require.Len(t, res.Delegations, 5) // 5 providers from redelegations totalDelegations := math.ZeroInt() @@ -495,7 +485,7 @@ func TestHooksRandomDelegations(t *testing.T) { delegatorAcc = prevDelegatorAcc delegator = prevDelegator } - _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(int64(d)))) + _, err := ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(int64(d)))) require.NoError(t, err) _, found := ts.Keepers.StakingKeeper.GetDelegation(ts.Ctx, delegatorAcc.Addr, sdk.ValAddress(validatorAcc.Addr)) @@ -533,7 +523,7 @@ func TestNotRoundedShares(t *testing.T) { require.NoError(t, err) ts.Keepers.StakingKeeper.SetDelegation(ts.Ctx, stakingtypes.NewDelegation(delegatorAcc.Addr, sdk.ValAddress(validatorAcc.Addr), shares)) - _, err = ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), delAmount)) + _, err = ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), delAmount)) require.NoError(t, err) } @@ -567,27 +557,11 @@ func TestUnbondValidatorButNotRemoveStakeEntry(t *testing.T) { require.NoError(t, err) } - providerAcct, provider := ts.GetAccount(common.PROVIDER, 0) + providerAcct, _ := ts.GetAccount(common.PROVIDER, 0) // provider completely unbond from validator, delegation is removed _, err = ts.TxUnbondValidator(*providerAcct.Vault, validator, sdk.NewInt(9999)) - require.NoError(t, err) - - // other delegator should not be able to delegate to the provider - _, err = ts.TxDualstakingRedelegate(delegatorAcc1.Addr.String(), - commontypes.EMPTY_PROVIDER, - provider, - commontypes.EMPTY_PROVIDER_CHAINID, - ts.spec.Index, - sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(9999))) require.Error(t, err) - - // checking that provider is not found - _, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) - require.False(t, found) - - _, err = ts.QueryDualstakingProviderDelegators(provider, true) - require.NoError(t, err) } // TestUndelegateProvider checks for a bug that when a provider unstakes, its delegations are not @@ -634,14 +608,12 @@ func TestUndelegateProvider(t *testing.T) { _, err = ts.TxDualstakingRedelegate(delegatorAcc1.Addr.String(), commontypes.EMPTY_PROVIDER, provider, - commontypes.EMPTY_PROVIDER_CHAINID, - ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(9999))) require.NoError(t, err) ts.AdvanceEpoch() - res2, err := ts.QueryDualstakingProviderDelegators(provider, true) + res2, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) fmt.Println("Delegation of Provider before provider is removed", res2) @@ -653,9 +625,9 @@ func TestUndelegateProvider(t *testing.T) { // delegation of the removed provider // the provider is removed but the delegation is still remained - res2, err = ts.QueryDualstakingProviderDelegators(provider, true) + res2, err = ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) - fmt.Println("Delegation of Provider after provider is removed", res2) + require.Equal(t, int64(9999), res2.Delegations[0].Amount.Amount.Int64()) ts.AdvanceEpochUntilStale() @@ -663,30 +635,25 @@ func TestUndelegateProvider(t *testing.T) { err = ts.StakeProvider(providerAcct.GetVaultAddr(), providerAcct.Addr.String(), ts.spec, sdk.NewIntFromUint64(1000).Int64()) require.NoError(t, err) - stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) + entry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) require.True(t, found) - fmt.Println("Stake entry of re-staked provider", stakeEntry.String()) + require.Equal(t, int64(9999), entry.DelegateTotal.Amount.Int64()) // delegator1 should be able to redelegate back to the empty provider _, err = ts.TxDualstakingRedelegate(delegatorAcc1.Addr.String(), provider, commontypes.EMPTY_PROVIDER, - ts.spec.Index, - commontypes.EMPTY_PROVIDER_CHAINID, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(1))) require.NoError(t, err) - stakeEntry, found = ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) + _, found = ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) require.True(t, found) - fmt.Println("Stake entry of re-staked provider after del1 9999 redelegation", stakeEntry.String()) // another one delegates to provider // delegator2 delegates 9998 to the provider _, err = ts.TxDualstakingRedelegate(delegatorAcc2.Addr.String(), commontypes.EMPTY_PROVIDER, provider, - commontypes.EMPTY_PROVIDER_CHAINID, - ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(9998))) require.NoError(t, err) @@ -696,16 +663,12 @@ func TestUndelegateProvider(t *testing.T) { _, err = ts.TxDualstakingRedelegate(delegatorAcc1.Addr.String(), provider, commontypes.EMPTY_PROVIDER, - ts.spec.Index, - commontypes.EMPTY_PROVIDER_CHAINID, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(9999))) require.Error(t, err) _, err = ts.TxDualstakingRedelegate(delegatorAcc1.Addr.String(), provider, commontypes.EMPTY_PROVIDER, - ts.spec.Index, - commontypes.EMPTY_PROVIDER_CHAINID, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(9998))) require.NoError(t, err) } diff --git a/x/dualstaking/keeper/keeper.go b/x/dualstaking/keeper/keeper.go index 4749bb3082..111b388935 100644 --- a/x/dualstaking/keeper/keeper.go +++ b/x/dualstaking/keeper/keeper.go @@ -2,16 +2,16 @@ package keeper import ( "fmt" - "strconv" + "cosmossdk.io/collections" abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - fixationtypes "github.com/lavanet/lava/v3/x/fixationstore/types" + collcompat "github.com/lavanet/lava/v3/utils/collcompat" "github.com/lavanet/lava/v3/x/dualstaking/types" ) @@ -28,8 +28,9 @@ type ( epochstorageKeeper types.EpochstorageKeeper specKeeper types.SpecKeeper - delegationFS fixationtypes.FixationStore // map proviers/chainID -> delegations - delegatorFS fixationtypes.FixationStore // map delegators -> providers + schema collections.Schema + delegations *collections.IndexedMap[collections.Pair[string, string], types.Delegation, types.DelegationIndexes] // key: [delegator, provider] + rewards collections.Map[collections.Pair[string, string], types.DelegatorReward] // key: [delegator, provider] } ) @@ -50,6 +51,8 @@ func NewKeeper( ps = ps.WithKeyTable(types.ParamKeyTable()) } + sb := collections.NewSchemaBuilder(collcompat.NewKVStoreService(storeKey)) + keeper := &Keeper{ cdc: cdc, storeKey: storeKey, @@ -61,50 +64,36 @@ func NewKeeper( accountKeeper: accountKeeper, epochstorageKeeper: epochstorageKeeper, specKeeper: specKeeper, - } - delegationFS := *fixationStoreKeeper.NewFixationStore(storeKey, types.DelegationPrefix) - delegatorFS := *fixationStoreKeeper.NewFixationStore(storeKey, types.DelegatorPrefix) + delegations: collections.NewIndexedMap(sb, types.DelegationsPrefix, "delegations", + collections.PairKeyCodec(collections.StringKey, collections.StringKey), + collcompat.ProtoValue[types.Delegation](cdc), + types.NewDelegationIndexes(sb)), - keeper.delegationFS = delegationFS - keeper.delegatorFS = delegatorFS + rewards: collections.NewMap(sb, types.RewardPrefix, "rewards", + collections.PairKeyCodec(collections.StringKey, collections.StringKey), + collcompat.ProtoValue[types.DelegatorReward](cdc)), + } + schema, err := sb.Build() + if err != nil { + panic(err) + } + keeper.schema = schema return keeper } -// ExportDelegations exports dualstaking delegations data (for genesis) -func (k Keeper) ExportDelegations(ctx sdk.Context) fixationtypes.GenesisState { - return k.delegationFS.Export(ctx) -} - -// ExportDelegators exports dualstaking delegators data (for genesis) -func (k Keeper) ExportDelegators(ctx sdk.Context) fixationtypes.GenesisState { - return k.delegatorFS.Export(ctx) -} - -// InitDelegations imports dualstaking delegations data (from genesis) -func (k Keeper) InitDelegations(ctx sdk.Context, data fixationtypes.GenesisState) { - k.delegationFS.Init(ctx, data) -} - -// InitDelegators imports dualstaking delegators data (from genesis) -func (k Keeper) InitDelegators(ctx sdk.Context, data fixationtypes.GenesisState) { - k.delegatorFS.Init(ctx, data) -} - func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } -func (k Keeper) ChangeDelegationTimestampForTesting(ctx sdk.Context, index string, block uint64, timestamp int64) error { - var d types.Delegation - entryBlock, _, _, found := k.delegationFS.FindEntryDetailed(ctx, index, block, &d) +func (k Keeper) ChangeDelegationTimestampForTesting(ctx sdk.Context, provider, delegator string, timestamp int64) error { + d, found := k.GetDelegation(ctx, provider, delegator) if !found { - return fmt.Errorf("cannot change delegation timestamp: delegation not found. index: %s, block: %s", index, strconv.FormatUint(block, 10)) + return fmt.Errorf("cannot change delegation timestamp: delegation not found. provider: %s, delegator: %s", provider, delegator) } d.Timestamp = timestamp - k.delegationFS.ModifyEntry(ctx, index, entryBlock, &d) - return nil + return k.SetDelegation(ctx, d) } func (k Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { diff --git a/x/dualstaking/keeper/migrations.go b/x/dualstaking/keeper/migrations.go index 3e8254a8d3..dcbfeb9604 100644 --- a/x/dualstaking/keeper/migrations.go +++ b/x/dualstaking/keeper/migrations.go @@ -5,9 +5,9 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - dualstakingv4 "github.com/lavanet/lava/v3/x/dualstaking/migrations/v4" - dualstakingtypes "github.com/lavanet/lava/v3/x/dualstaking/types" - "github.com/lavanet/lava/v3/x/pairing/types" + "github.com/lavanet/lava/v3/x/dualstaking/types" + fixationtypes "github.com/lavanet/lava/v3/x/fixationstore/types" + timerstoretypes "github.com/lavanet/lava/v3/x/timerstore/types" ) type Migrator struct { @@ -18,36 +18,69 @@ func NewMigrator(keeper Keeper) Migrator { return Migrator{keeper: keeper} } -// MigrateVersion4To5 change the DelegatorReward object amount's field from sdk.Coin to sdk.Coins -func (m Migrator) MigrateVersion4To5(ctx sdk.Context) error { - delegatorRewards := m.GetAllDelegatorRewardV4(ctx) - for _, dr := range delegatorRewards { - delegatorReward := dualstakingtypes.DelegatorReward{ - Delegator: dr.Delegator, - Provider: dr.Provider, - ChainId: dr.ChainId, - Amount: sdk.NewCoins(dr.Amount), +func (m Migrator) MigrateVersion5To6(ctx sdk.Context) error { + nextEpoch := m.keeper.epochstorageKeeper.GetCurrentNextEpoch(ctx) + + // prefix for the delegations fixation store + const ( + DelegationPrefix = "delegation-fs" + DelegatorPrefix = "delegator-fs" + UnbondingPrefix = "unbonding-ts" + DelegatorRewardKeyPrefix = "DelegatorReward/value/" + ) + + // set delegations + ts := timerstoretypes.NewTimerStore(m.keeper.storeKey, m.keeper.cdc, DelegationPrefix) + delegationFS := fixationtypes.NewFixationStore(m.keeper.storeKey, m.keeper.cdc, DelegationPrefix, ts, nil) + incisec := delegationFS.GetAllEntryIndices(ctx) + for _, index := range incisec { + var oldDelegation types.Delegation + found := delegationFS.FindEntry(ctx, index, nextEpoch, &oldDelegation) + if found { + delegation, found := m.keeper.GetDelegation(ctx, oldDelegation.Provider, oldDelegation.Delegator) + if found { + delegation.Amount = delegation.Amount.Add(oldDelegation.Amount) + } else { + delegation = oldDelegation + } + m.keeper.SetDelegation(ctx, delegation) } - m.keeper.RemoveDelegatorReward(ctx, dualstakingtypes.DelegationKey(dr.Provider, dr.Delegator, dr.ChainId)) - m.keeper.SetDelegatorReward(ctx, delegatorReward) } - params := dualstakingtypes.DefaultParams() - m.keeper.SetParams(ctx, params) - return nil -} - -func (m Migrator) GetAllDelegatorRewardV4(ctx sdk.Context) (list []dualstakingv4.DelegatorRewardv4) { - store := prefix.NewStore(ctx.KVStore(m.keeper.storeKey), types.KeyPrefix(dualstakingtypes.DelegatorRewardKeyPrefix)) + // set rewards + store := prefix.NewStore(ctx.KVStore(m.keeper.storeKey), types.KeyPrefix(DelegatorRewardKeyPrefix)) iterator := sdk.KVStorePrefixIterator(store, []byte{}) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - var val dualstakingv4.DelegatorRewardv4 + var val types.DelegatorReward m.keeper.cdc.MustUnmarshal(iterator.Value(), &val) - list = append(list, val) + reward, found := m.keeper.GetDelegatorReward(ctx, val.Delegator, val.Delegator) + if found { + reward.Amount = reward.Amount.Add(val.Amount...) + } else { + reward = val + } + m.keeper.SetDelegatorReward(ctx, reward) } - return + // now delete the stores + deleteStore := func(prefixString string) { + store := prefix.NewStore(ctx.KVStore(m.keeper.storeKey), types.KeyPrefix(prefixString)) + iterator := sdk.KVStorePrefixIterator(store, []byte{}) + + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + store.Delete(iterator.Key()) + } + } + + deleteStore(DelegationPrefix) + deleteStore(DelegatorPrefix) + deleteStore(UnbondingPrefix) + deleteStore(DelegatorRewardKeyPrefix) + + return nil } diff --git a/x/dualstaking/keeper/msg_server_delegate.go b/x/dualstaking/keeper/msg_server_delegate.go index 0ce56b460a..eeeab8ab58 100644 --- a/x/dualstaking/keeper/msg_server_delegate.go +++ b/x/dualstaking/keeper/msg_server_delegate.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "fmt" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -13,17 +12,11 @@ import ( func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*types.MsgDelegateResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - return &types.MsgDelegateResponse{}, k.Keeper.DelegateFull(ctx, msg.Creator, msg.Validator, msg.Provider, msg.ChainID, msg.Amount) + return &types.MsgDelegateResponse{}, k.Keeper.DelegateFull(ctx, msg.Creator, msg.Validator, msg.Provider, msg.Amount, false) } // DelegateFull uses staking module for to delegate with hooks -func (k Keeper) DelegateFull(ctx sdk.Context, delegator string, validator string, provider string, chainID string, amount sdk.Coin) error { - _, found := k.specKeeper.GetSpec(ctx, chainID) - if !found && chainID != commontypes.EMPTY_PROVIDER_CHAINID { - return utils.LavaFormatWarning("invalid chain ID", fmt.Errorf("chain ID not found"), - utils.LogAttr("chain_id", chainID)) - } - +func (k Keeper) DelegateFull(ctx sdk.Context, delegator string, validator string, provider string, amount sdk.Coin, stake bool) error { valAddr, valErr := sdk.ValAddressFromBech32(validator) if valErr != nil { return valErr @@ -47,9 +40,7 @@ func (k Keeper) DelegateFull(ctx sdk.Context, delegator string, validator string return err } - nextEpoch := k.epochstorageKeeper.GetCurrentNextEpoch(ctx) - - delegation, found := k.GetDelegation(ctx, delegator, commontypes.EMPTY_PROVIDER, commontypes.EMPTY_PROVIDER_CHAINID, nextEpoch) + delegation, found := k.GetDelegation(ctx, commontypes.EMPTY_PROVIDER, delegator) amountBefore := sdk.ZeroInt() if found { amountBefore = delegation.Amount.Amount @@ -60,7 +51,7 @@ func (k Keeper) DelegateFull(ctx sdk.Context, delegator string, validator string return err } - delegation, _ = k.GetDelegation(ctx, delegator, commontypes.EMPTY_PROVIDER, commontypes.EMPTY_PROVIDER_CHAINID, nextEpoch) + delegation, _ = k.GetDelegation(ctx, commontypes.EMPTY_PROVIDER, delegator) amount.Amount = delegation.Amount.Amount.Sub(amountBefore) @@ -69,9 +60,8 @@ func (k Keeper) DelegateFull(ctx sdk.Context, delegator string, validator string delegator, commontypes.EMPTY_PROVIDER, provider, - commontypes.EMPTY_PROVIDER_CHAINID, - chainID, amount, + stake, ) if err == nil { @@ -79,7 +69,6 @@ func (k Keeper) DelegateFull(ctx sdk.Context, delegator string, validator string details := map[string]string{ "delegator": delegator, "provider": provider, - "chainID": chainID, "amount": amount.String(), } utils.LogLavaEvent(ctx, logger, types.DelegateEventName, details, "Delegate") diff --git a/x/dualstaking/keeper/msg_server_redelegate.go b/x/dualstaking/keeper/msg_server_redelegate.go index 4e3c112b17..2f11eaaa46 100644 --- a/x/dualstaking/keeper/msg_server_redelegate.go +++ b/x/dualstaking/keeper/msg_server_redelegate.go @@ -5,7 +5,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v3/utils" - commontypes "github.com/lavanet/lava/v3/utils/common/types" "github.com/lavanet/lava/v3/x/dualstaking/types" ) @@ -20,22 +19,13 @@ func (k msgServer) Redelegate(goCtx context.Context, msg *types.MsgRedelegate) ( return &types.MsgRedelegateResponse{}, err } - // allow non empty chainID calls to redelegate form empty provider - if msg.FromChainID == commontypes.EMPTY_PROVIDER_CHAINID_STAR { - msg.FromChainID = commontypes.EMPTY_PROVIDER_CHAINID - } - if msg.ToChainID == commontypes.EMPTY_PROVIDER_CHAINID_STAR { - msg.ToChainID = commontypes.EMPTY_PROVIDER_CHAINID - } - err := k.Keeper.Redelegate( ctx, msg.Creator, msg.FromProvider, msg.ToProvider, - msg.FromChainID, - msg.ToChainID, msg.Amount, + false, ) if err == nil { @@ -44,8 +34,6 @@ func (k msgServer) Redelegate(goCtx context.Context, msg *types.MsgRedelegate) ( "delegator": msg.Creator, "from_provider": msg.FromProvider, "to_provider": msg.ToProvider, - "from_chainID": msg.FromChainID, - "to_chainID": msg.ToChainID, "amount": msg.Amount.String(), } utils.LogLavaEvent(ctx, logger, types.RedelegateEventName, details, "Redelegate") diff --git a/x/dualstaking/keeper/msg_server_unbond.go b/x/dualstaking/keeper/msg_server_unbond.go index d195fbf80e..1d79bfe5b3 100644 --- a/x/dualstaking/keeper/msg_server_unbond.go +++ b/x/dualstaking/keeper/msg_server_unbond.go @@ -11,11 +11,11 @@ import ( func (k msgServer) Unbond(goCtx context.Context, msg *types.MsgUnbond) (*types.MsgUnbondResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - return &types.MsgUnbondResponse{}, k.Keeper.UnbondFull(ctx, msg.Creator, msg.Validator, msg.Provider, msg.ChainID, msg.Amount, false) + return &types.MsgUnbondResponse{}, k.Keeper.UnbondFull(ctx, msg.Creator, msg.Validator, msg.Provider, msg.Amount, false) } // UnbondFul uses staking module for to unbond with hooks -func (k Keeper) UnbondFull(ctx sdk.Context, delegator string, validator string, provider string, chainID string, amount sdk.Coin, unstake bool) error { +func (k Keeper) UnbondFull(ctx sdk.Context, delegator string, validator string, provider string, amount sdk.Coin, stake bool) error { // 1.redelegate from the provider to the empty provider // 2.calls staking module to unbond from the validator // 3.calls the hooks to than unbond from the empty provider @@ -45,9 +45,8 @@ func (k Keeper) UnbondFull(ctx sdk.Context, delegator string, validator string, delegator, provider, commontypes.EMPTY_PROVIDER, - chainID, - commontypes.EMPTY_PROVIDER_CHAINID, amount, + stake, ) if err != nil { return err @@ -61,7 +60,6 @@ func (k Keeper) UnbondFull(ctx sdk.Context, delegator string, validator string, details := map[string]string{ "delegator": delegator, "provider": provider, - "chainID": chainID, "amount": amount.String(), } utils.LogLavaEvent(ctx, logger, types.UnbondingEventName, details, "Unbond") diff --git a/x/dualstaking/module.go b/x/dualstaking/module.go index 977359096f..31f43c1546 100644 --- a/x/dualstaking/module.go +++ b/x/dualstaking/module.go @@ -118,8 +118,8 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { migrator := keeper.NewMigrator(am.keeper) - // register v4 -> v5 migration - if err := cfg.RegisterMigration(types.ModuleName, 4, migrator.MigrateVersion4To5); err != nil { + // register v5 -> v6 migration + if err := cfg.RegisterMigration(types.ModuleName, 5, migrator.MigrateVersion5To6); err != nil { // panic:ok: at start up, migration cannot proceed anyhow panic(fmt.Errorf("%s: failed to register migration to v5: %w", types.ModuleName, err)) } @@ -146,7 +146,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1 -func (AppModule) ConsensusVersion() uint64 { return 5 } +func (AppModule) ConsensusVersion() uint64 { return 6 } // BeginBlock contains the logic that is automatically triggered at the beginning of each block func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { diff --git a/x/dualstaking/types/delegate.go b/x/dualstaking/types/delegate.go index 420ebc5f70..db934b9b51 100644 --- a/x/dualstaking/types/delegate.go +++ b/x/dualstaking/types/delegate.go @@ -3,16 +3,36 @@ package types import ( "time" + "cosmossdk.io/collections" + "cosmossdk.io/collections/indexes" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v3/utils" "github.com/lavanet/lava/v3/utils/lavaslices" ) -func NewDelegation(delegator, provider, chainID string, blockTime time.Time, tokenDenom string) Delegation { +var DelegationIndexPrefix = collections.NewPrefix(1) + +type DelegationIndexes struct { + ReverseIndex *indexes.ReversePair[string, string, Delegation] +} + +func (a DelegationIndexes) IndexesList() []collections.Index[collections.Pair[string, string], Delegation] { + return []collections.Index[collections.Pair[string, string], Delegation]{a.ReverseIndex} +} + +func NewDelegationIndexes(sb *collections.SchemaBuilder) DelegationIndexes { + return DelegationIndexes{ + ReverseIndex: indexes.NewReversePair[Delegation]( + sb, DelegationIndexPrefix, "delegation_by_provider_delegator", + collections.PairKeyCodec(collections.StringKey, collections.StringKey), + ), + } +} + +func NewDelegation(delegator, provider string, blockTime time.Time, tokenDenom string) Delegation { return Delegation{ Delegator: delegator, Provider: provider, - ChainID: chainID, Amount: sdk.NewCoin(tokenDenom, sdk.ZeroInt()), Timestamp: utils.NextMonth(blockTime).UTC().Unix(), } @@ -33,7 +53,6 @@ func (delegation *Delegation) IsZero() bool { func (delegation *Delegation) Equal(other *Delegation) bool { if delegation.Delegator != other.Delegator || delegation.Provider != other.Provider || - delegation.ChainID != other.ChainID || !delegation.Amount.IsEqual(other.Amount) { return false } diff --git a/x/dualstaking/types/delegate.pb.go b/x/dualstaking/types/delegate.pb.go index 3e04209a2b..34ec8b0c40 100644 --- a/x/dualstaking/types/delegate.pb.go +++ b/x/dualstaking/types/delegate.pb.go @@ -26,7 +26,6 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Delegation 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"` Delegator string `protobuf:"bytes,3,opt,name=delegator,proto3" json:"delegator,omitempty"` Amount types.Coin `protobuf:"bytes,4,opt,name=amount,proto3" json:"amount"` Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` @@ -72,13 +71,6 @@ func (m *Delegation) GetProvider() string { return "" } -func (m *Delegation) GetChainID() string { - if m != nil { - return m.ChainID - } - return "" -} - func (m *Delegation) GetDelegator() string { if m != nil { return m.Delegator @@ -154,27 +146,26 @@ func init() { } var fileDescriptor_547eac7f30bf94d4 = []byte{ - // 306 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x91, 0xb1, 0x4e, 0xc3, 0x30, - 0x10, 0x86, 0x63, 0x5a, 0x0a, 0x31, 0x5b, 0xc4, 0x60, 0xaa, 0xca, 0x44, 0x5d, 0x28, 0x8b, 0xad, - 0xd2, 0x81, 0xbd, 0x74, 0x80, 0xb5, 0x23, 0x9b, 0x93, 0x5a, 0xa9, 0x45, 0xe3, 0x8b, 0x62, 0x37, - 0x82, 0xb7, 0xe0, 0x6d, 0x78, 0x85, 0x8e, 0x1d, 0x99, 0x10, 0x4a, 0x5e, 0x04, 0x39, 0x49, 0x53, - 0x3a, 0x9d, 0xef, 0xff, 0x7f, 0xf9, 0x3e, 0xdd, 0xe1, 0xbb, 0x8d, 0x28, 0x84, 0x96, 0x96, 0xbb, - 0xca, 0x57, 0x5b, 0xb1, 0x31, 0x56, 0xbc, 0x29, 0x9d, 0xf0, 0x95, 0xdc, 0xc8, 0x44, 0x58, 0xc9, - 0xb2, 0x1c, 0x2c, 0x04, 0xa4, 0x0d, 0x32, 0x57, 0xd9, 0xbf, 0xe0, 0xf0, 0x3a, 0x81, 0x04, 0xea, - 0x10, 0x77, 0xaf, 0x26, 0x3f, 0xa4, 0x31, 0x98, 0x14, 0x0c, 0x8f, 0x84, 0x91, 0xbc, 0x98, 0x46, - 0xd2, 0x8a, 0x29, 0x8f, 0x41, 0xe9, 0xc6, 0x1f, 0x7f, 0x21, 0x8c, 0x17, 0xcd, 0x08, 0x05, 0x3a, - 0x18, 0xe2, 0xcb, 0x2c, 0x87, 0x42, 0xad, 0x64, 0x4e, 0x50, 0x88, 0x26, 0xfe, 0xb2, 0xeb, 0x03, - 0x82, 0x2f, 0xe2, 0xb5, 0x50, 0xfa, 0x65, 0x41, 0xce, 0x6a, 0xeb, 0xd0, 0x06, 0x23, 0xec, 0xb7, - 0x98, 0x90, 0x93, 0x5e, 0xed, 0x1d, 0x85, 0xe0, 0x11, 0x0f, 0x44, 0x0a, 0x5b, 0x6d, 0x49, 0x3f, - 0x44, 0x93, 0xab, 0x87, 0x1b, 0xd6, 0x30, 0x31, 0xc7, 0xc4, 0x5a, 0x26, 0xf6, 0x04, 0x4a, 0xcf, - 0xfb, 0xbb, 0x9f, 0x5b, 0x6f, 0xd9, 0xc6, 0xdd, 0xb7, 0x56, 0xa5, 0xd2, 0x58, 0x91, 0x66, 0xe4, - 0x3c, 0x44, 0x93, 0xde, 0xf2, 0x28, 0x8c, 0xef, 0xb1, 0xbf, 0xe8, 0x66, 0x8c, 0xb0, 0x7f, 0xe0, - 0x34, 0x04, 0x85, 0x3d, 0x47, 0xd0, 0x09, 0xf3, 0xe7, 0x5d, 0x49, 0xd1, 0xbe, 0xa4, 0xe8, 0xb7, - 0xa4, 0xe8, 0xb3, 0xa2, 0xde, 0xbe, 0xa2, 0xde, 0x77, 0x45, 0xbd, 0x57, 0x96, 0x28, 0xbb, 0xde, - 0x46, 0x2c, 0x86, 0x94, 0x9f, 0x9c, 0xa0, 0x98, 0xf1, 0xf7, 0x93, 0x3b, 0xd8, 0x8f, 0x4c, 0x9a, - 0x68, 0x50, 0x6f, 0x6d, 0xf6, 0x17, 0x00, 0x00, 0xff, 0xff, 0x90, 0xb7, 0x12, 0x3c, 0xb0, 0x01, - 0x00, 0x00, + // 300 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xb1, 0x4e, 0xf3, 0x30, + 0x14, 0x85, 0xe3, 0x3f, 0xfd, 0xab, 0xc6, 0x2c, 0x28, 0x62, 0x30, 0x55, 0x65, 0xa2, 0x2e, 0x94, + 0xc5, 0x56, 0xe9, 0xc0, 0x5e, 0x3a, 0x20, 0xc6, 0x8e, 0x6c, 0x4e, 0x6b, 0x05, 0x8b, 0xc6, 0x37, + 0x8a, 0x9d, 0x08, 0xde, 0x82, 0x77, 0xe0, 0x65, 0x3a, 0x76, 0x64, 0x42, 0x28, 0x79, 0x11, 0xe4, + 0x24, 0xa4, 0x74, 0xba, 0xf6, 0xb9, 0x47, 0xf7, 0x3b, 0x3a, 0xf8, 0x7a, 0x27, 0x4a, 0xa1, 0xa5, + 0xe5, 0x6e, 0xf2, 0x6d, 0x21, 0x76, 0xc6, 0x8a, 0x17, 0xa5, 0x13, 0xbe, 0x95, 0x3b, 0x99, 0x08, + 0x2b, 0x59, 0x96, 0x83, 0x85, 0x90, 0x74, 0x46, 0xe6, 0x26, 0xfb, 0x63, 0x1c, 0x5f, 0x24, 0x90, + 0x40, 0x63, 0xe2, 0xee, 0xd5, 0xfa, 0xc7, 0x74, 0x03, 0x26, 0x05, 0xc3, 0x63, 0x61, 0x24, 0x2f, + 0xe7, 0xb1, 0xb4, 0x62, 0xce, 0x37, 0xa0, 0x74, 0xbb, 0x9f, 0x7e, 0x20, 0x8c, 0x57, 0x2d, 0x42, + 0x81, 0x0e, 0xc7, 0x78, 0x94, 0xe5, 0x50, 0xaa, 0xad, 0xcc, 0x09, 0x8a, 0xd0, 0x2c, 0x58, 0xf7, + 0xff, 0x70, 0x82, 0x83, 0x2e, 0x0c, 0xe4, 0xc4, 0x6f, 0x96, 0x47, 0x21, 0xbc, 0xc3, 0x43, 0x91, + 0x42, 0xa1, 0x2d, 0x19, 0x44, 0x68, 0x76, 0x76, 0x7b, 0xc9, 0x5a, 0x32, 0x73, 0x64, 0xd6, 0x91, + 0xd9, 0x3d, 0x28, 0xbd, 0x1c, 0xec, 0xbf, 0xae, 0xbc, 0x75, 0x67, 0x77, 0x67, 0xad, 0x4a, 0xa5, + 0xb1, 0x22, 0xcd, 0xc8, 0xff, 0x08, 0xcd, 0xfc, 0xf5, 0x51, 0x78, 0x1c, 0x8c, 0xfe, 0x9d, 0xfb, + 0xd3, 0x1b, 0x1c, 0xac, 0x7a, 0xd2, 0x04, 0x07, 0xbf, 0x99, 0x0c, 0x41, 0x91, 0xef, 0x72, 0xf4, + 0xc2, 0xf2, 0x61, 0x5f, 0x51, 0x74, 0xa8, 0x28, 0xfa, 0xae, 0x28, 0x7a, 0xaf, 0xa9, 0x77, 0xa8, + 0xa9, 0xf7, 0x59, 0x53, 0xef, 0x89, 0x25, 0xca, 0x3e, 0x17, 0x31, 0xdb, 0x40, 0xca, 0x4f, 0xea, + 0x2e, 0x17, 0xfc, 0xf5, 0xa4, 0x73, 0xfb, 0x96, 0x49, 0x13, 0x0f, 0x9b, 0x86, 0x16, 0x3f, 0x01, + 0x00, 0x00, 0xff, 0xff, 0x2a, 0xdf, 0x39, 0x25, 0x9c, 0x01, 0x00, 0x00, } func (m *Delegation) Marshal() (dAtA []byte, err error) { @@ -219,13 +210,6 @@ func (m *Delegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - if len(m.ChainID) > 0 { - i -= len(m.ChainID) - copy(dAtA[i:], m.ChainID) - i = encodeVarintDelegate(dAtA, i, uint64(len(m.ChainID))) - i-- - dAtA[i] = 0x12 - } if len(m.Provider) > 0 { i -= len(m.Provider) copy(dAtA[i:], m.Provider) @@ -289,10 +273,6 @@ func (m *Delegation) Size() (n int) { if l > 0 { n += 1 + l + sovDelegate(uint64(l)) } - l = len(m.ChainID) - if l > 0 { - n += 1 + l + sovDelegate(uint64(l)) - } l = len(m.Delegator) if l > 0 { n += 1 + l + sovDelegate(uint64(l)) @@ -387,38 +367,6 @@ func (m *Delegation) Unmarshal(dAtA []byte) error { } 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 ErrIntOverflowDelegate - } - 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 ErrInvalidLengthDelegate - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthDelegate - } - 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 Delegator", wireType) diff --git a/x/dualstaking/types/delegator_reward.pb.go b/x/dualstaking/types/delegator_reward.pb.go index 3f6469da51..d7e4c02c44 100644 --- a/x/dualstaking/types/delegator_reward.pb.go +++ b/x/dualstaking/types/delegator_reward.pb.go @@ -28,7 +28,6 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type DelegatorReward struct { Delegator string `protobuf:"bytes,1,opt,name=delegator,proto3" json:"delegator,omitempty"` Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` - ChainId string `protobuf:"bytes,3,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` } @@ -79,13 +78,6 @@ func (m *DelegatorReward) GetProvider() string { return "" } -func (m *DelegatorReward) GetChainId() string { - if m != nil { - return m.ChainId - } - return "" -} - func (m *DelegatorReward) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { if m != nil { return m.Amount @@ -102,26 +94,26 @@ func init() { } var fileDescriptor_c8b6da054bf40d1f = []byte{ - // 301 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x50, 0xbd, 0x4e, 0xf3, 0x30, - 0x14, 0x8d, 0xbf, 0x7e, 0x2a, 0xad, 0x19, 0x90, 0x22, 0x86, 0xb4, 0x42, 0x6e, 0xc5, 0xd4, 0x05, - 0x9b, 0xd2, 0x37, 0x28, 0x0c, 0xb0, 0x76, 0x64, 0xa9, 0x9c, 0xd8, 0x4a, 0xad, 0xb6, 0xbe, 0x95, - 0xed, 0x04, 0x78, 0x0b, 0x9e, 0x83, 0x27, 0xa9, 0xc4, 0xd2, 0x91, 0x09, 0x50, 0xf2, 0x22, 0x28, - 0x4e, 0x28, 0xed, 0x74, 0x73, 0x73, 0xee, 0xf9, 0xf1, 0xc1, 0x6c, 0xc5, 0x73, 0xae, 0xa5, 0xf3, - 0x93, 0x89, 0x8c, 0xaf, 0xac, 0xe3, 0x4b, 0xa5, 0x53, 0x26, 0xe4, 0x4a, 0xa6, 0xdc, 0x81, 0x99, - 0x1b, 0xf9, 0xc4, 0x8d, 0xa0, 0x1b, 0x03, 0x0e, 0xc2, 0xa8, 0x21, 0xd0, 0x6a, 0xd2, 0x03, 0x42, - 0xff, 0x3c, 0x85, 0x14, 0xfc, 0x11, 0xab, 0xbe, 0xea, 0xfb, 0x3e, 0x49, 0xc0, 0xae, 0xc1, 0xb2, - 0x98, 0x5b, 0xc9, 0xf2, 0x71, 0x2c, 0x1d, 0x1f, 0xb3, 0x04, 0x94, 0xae, 0xf1, 0xcb, 0x77, 0x84, - 0xcf, 0xee, 0x7e, 0xad, 0x66, 0xde, 0x29, 0xbc, 0xc0, 0xdd, 0xbd, 0x7b, 0x84, 0x86, 0x68, 0xd4, - 0x9d, 0xfd, 0xfd, 0x08, 0xfb, 0xb8, 0xb3, 0x31, 0x90, 0x2b, 0x21, 0x4d, 0xf4, 0xcf, 0x83, 0xfb, - 0x3d, 0xec, 0xe1, 0x4e, 0xb2, 0xe0, 0x4a, 0xcf, 0x95, 0x88, 0x5a, 0x1e, 0x3b, 0xf1, 0xfb, 0x83, - 0x08, 0x13, 0xdc, 0xe6, 0x6b, 0xc8, 0xb4, 0x8b, 0xfe, 0x0f, 0x5b, 0xa3, 0xd3, 0x9b, 0x1e, 0xad, - 0x93, 0xd1, 0x2a, 0x19, 0x6d, 0x92, 0xd1, 0x5b, 0x50, 0x7a, 0x7a, 0xbd, 0xfd, 0x1c, 0x04, 0x6f, - 0x5f, 0x83, 0x51, 0xaa, 0xdc, 0x22, 0x8b, 0x69, 0x02, 0x6b, 0xd6, 0x3c, 0xa3, 0x1e, 0x57, 0x56, - 0x2c, 0x99, 0x7b, 0xd9, 0x48, 0xeb, 0x09, 0x76, 0xd6, 0x48, 0x4f, 0xef, 0xb7, 0x05, 0x41, 0xbb, - 0x82, 0xa0, 0xef, 0x82, 0xa0, 0xd7, 0x92, 0x04, 0xbb, 0x92, 0x04, 0x1f, 0x25, 0x09, 0x1e, 0xe9, - 0x81, 0xd6, 0x51, 0xe7, 0xf9, 0x84, 0x3d, 0x1f, 0x15, 0xef, 0x75, 0xe3, 0xb6, 0xaf, 0x67, 0xf2, - 0x13, 0x00, 0x00, 0xff, 0xff, 0x46, 0xff, 0xf5, 0xaa, 0xa1, 0x01, 0x00, 0x00, + // 291 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0x31, 0x4e, 0xfb, 0x30, + 0x18, 0xc5, 0xe3, 0x7f, 0xa3, 0xaa, 0xcd, 0x7f, 0x00, 0x45, 0x0c, 0x21, 0x42, 0x6e, 0xc5, 0x94, + 0x05, 0x9b, 0xd2, 0x1b, 0x14, 0x06, 0xc4, 0x98, 0x91, 0x05, 0x39, 0x89, 0x15, 0xa2, 0x26, 0xfe, + 0x22, 0xdb, 0x09, 0x70, 0x0b, 0xce, 0xc1, 0x01, 0x38, 0x43, 0xc7, 0x8e, 0x4c, 0x80, 0x92, 0x8b, + 0xa0, 0x38, 0xa1, 0xb4, 0xd3, 0xb3, 0xfd, 0x7d, 0xef, 0xfd, 0xac, 0xe7, 0xd0, 0x9c, 0xd5, 0x4c, + 0x70, 0x6d, 0x94, 0x26, 0x15, 0xcb, 0x95, 0x66, 0xeb, 0x4c, 0xa4, 0x34, 0xe1, 0x39, 0x4f, 0x99, + 0x06, 0xf9, 0x20, 0xf9, 0x13, 0x93, 0x09, 0x29, 0x25, 0x68, 0x70, 0xbd, 0xc1, 0x40, 0x3a, 0x25, + 0x7b, 0x06, 0xff, 0x24, 0x85, 0x14, 0xcc, 0x12, 0xed, 0x4e, 0xfd, 0xbe, 0x8f, 0x63, 0x50, 0x05, + 0x28, 0x1a, 0x31, 0xc5, 0x69, 0xbd, 0x88, 0xb8, 0x66, 0x0b, 0x1a, 0x43, 0x26, 0xfa, 0xf9, 0xf9, + 0x3b, 0x72, 0x8e, 0x6e, 0x7e, 0x51, 0xa1, 0x21, 0xb9, 0x67, 0xce, 0x74, 0x47, 0xf7, 0xd0, 0x1c, + 0x05, 0xd3, 0xf0, 0xef, 0xc1, 0xf5, 0x9d, 0x49, 0x29, 0xa1, 0xce, 0x12, 0x2e, 0xbd, 0x7f, 0x66, + 0xb8, 0xbb, 0xbb, 0xb1, 0x33, 0x66, 0x05, 0x54, 0x42, 0x7b, 0xf6, 0x7c, 0x14, 0xfc, 0xbf, 0x3a, + 0x25, 0x3d, 0x9e, 0x74, 0x78, 0x32, 0xe0, 0xc9, 0x35, 0x64, 0x62, 0x75, 0xb9, 0xf9, 0x9c, 0x59, + 0x6f, 0x5f, 0xb3, 0x20, 0xcd, 0xf4, 0x63, 0x15, 0x91, 0x18, 0x0a, 0x3a, 0xfc, 0xb5, 0x97, 0x0b, + 0x95, 0xac, 0xa9, 0x7e, 0x29, 0xb9, 0x32, 0x06, 0x15, 0x0e, 0xd1, 0x77, 0xf6, 0x64, 0x74, 0x6c, + 0xaf, 0x6e, 0x37, 0x0d, 0x46, 0xdb, 0x06, 0xa3, 0xef, 0x06, 0xa3, 0xd7, 0x16, 0x5b, 0xdb, 0x16, + 0x5b, 0x1f, 0x2d, 0xb6, 0xee, 0xc9, 0x5e, 0xe2, 0x41, 0xbd, 0xf5, 0x92, 0x3e, 0x1f, 0x74, 0x6c, + 0xd2, 0xa3, 0xb1, 0x69, 0x62, 0xf9, 0x13, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x84, 0xf0, 0x2c, 0x8c, + 0x01, 0x00, 0x00, } func (m *DelegatorReward) Marshal() (dAtA []byte, err error) { @@ -158,13 +150,6 @@ func (m *DelegatorReward) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x22 } } - if len(m.ChainId) > 0 { - i -= len(m.ChainId) - copy(dAtA[i:], m.ChainId) - i = encodeVarintDelegatorReward(dAtA, i, uint64(len(m.ChainId))) - i-- - dAtA[i] = 0x1a - } if len(m.Provider) > 0 { i -= len(m.Provider) copy(dAtA[i:], m.Provider) @@ -207,10 +192,6 @@ func (m *DelegatorReward) Size() (n int) { if l > 0 { n += 1 + l + sovDelegatorReward(uint64(l)) } - l = len(m.ChainId) - if l > 0 { - n += 1 + l + sovDelegatorReward(uint64(l)) - } if len(m.Amount) > 0 { for _, e := range m.Amount { l = e.Size() @@ -319,38 +300,6 @@ func (m *DelegatorReward) Unmarshal(dAtA []byte) error { } m.Provider = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - 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 ErrIntOverflowDelegatorReward - } - 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 ErrInvalidLengthDelegatorReward - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthDelegatorReward - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ChainId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) diff --git a/x/dualstaking/types/expected_keepers.go b/x/dualstaking/types/expected_keepers.go index cd454a6c2f..e6e3851470 100644 --- a/x/dualstaking/types/expected_keepers.go +++ b/x/dualstaking/types/expected_keepers.go @@ -38,6 +38,8 @@ type EpochstorageKeeper interface { GetCurrentNextEpoch(ctx sdk.Context) (nextEpoch uint64) RemoveStakeEntryCurrent(ctx sdk.Context, chainID string, address string) GetStakeEntry(ctx sdk.Context, epoch uint64, chainID string, provider string) (val epochstoragetypes.StakeEntry, found bool) + GetMetadata(ctx sdk.Context, provider string) (epochstoragetypes.ProviderMetadata, error) + SetMetadata(ctx sdk.Context, metadata epochstoragetypes.ProviderMetadata) // Methods imported from epochstorage should be defined here } diff --git a/x/dualstaking/types/genesis.go b/x/dualstaking/types/genesis.go index aefa38b541..1dc916c630 100644 --- a/x/dualstaking/types/genesis.go +++ b/x/dualstaking/types/genesis.go @@ -2,8 +2,6 @@ package types import ( fmt "fmt" - - fixationstoretypes "github.com/lavanet/lava/v3/x/fixationstore/types" ) // DefaultIndex is the default global index @@ -15,8 +13,7 @@ func DefaultGenesis() *GenesisState { // this line is used by starport scaffolding # genesis/types/default Params: DefaultParams(), DelegatorRewardList: []DelegatorReward{}, - DelegationsFS: *fixationstoretypes.DefaultGenesis(), - DelegatorsFS: *fixationstoretypes.DefaultGenesis(), + Delegations: []Delegation{}, } } @@ -27,12 +24,23 @@ func (gs GenesisState) Validate() error { delegatorRewardIndexMap := make(map[string]struct{}) for _, elem := range gs.DelegatorRewardList { - index := DelegationKey(elem.Provider, elem.Delegator, elem.ChainId) + key := DelegationKey(elem.Provider, elem.Delegator) + index := key.K1() + key.K2() if _, ok := delegatorRewardIndexMap[index]; ok { return fmt.Errorf("duplicated index for delegatorReward") } delegatorRewardIndexMap[index] = struct{}{} } + + delegationIndexMap := make(map[string]struct{}) + for _, elem := range gs.Delegations { + key := DelegationKey(elem.Provider, elem.Delegator) + index := key.K1() + key.K2() + if _, ok := delegationIndexMap[index]; ok { + return fmt.Errorf("duplicated index for delegations") + } + delegationIndexMap[index] = struct{}{} + } // this line is used by starport scaffolding # genesis/types/validate return gs.Params.Validate() diff --git a/x/dualstaking/types/genesis.pb.go b/x/dualstaking/types/genesis.pb.go index 1c48861d97..672d9c0e41 100644 --- a/x/dualstaking/types/genesis.pb.go +++ b/x/dualstaking/types/genesis.pb.go @@ -7,8 +7,6 @@ import ( fmt "fmt" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" - types "github.com/lavanet/lava/v3/x/fixationstore/types" - _ "github.com/lavanet/lava/v3/x/timerstore/types" io "io" math "math" math_bits "math/bits" @@ -27,10 +25,9 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState defines the dualstaking module's genesis state. type GenesisState struct { - Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` - DelegationsFS types.GenesisState `protobuf:"bytes,2,opt,name=delegationsFS,proto3" json:"delegationsFS"` - DelegatorsFS types.GenesisState `protobuf:"bytes,3,opt,name=delegatorsFS,proto3" json:"delegatorsFS"` - DelegatorRewardList []DelegatorReward `protobuf:"bytes,5,rep,name=delegator_reward_list,json=delegatorRewardList,proto3" json:"delegator_reward_list"` + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + DelegatorRewardList []DelegatorReward `protobuf:"bytes,5,rep,name=delegator_reward_list,json=delegatorRewardList,proto3" json:"delegator_reward_list"` + Delegations []Delegation `protobuf:"bytes,6,rep,name=Delegations,proto3" json:"Delegations"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -73,23 +70,16 @@ func (m *GenesisState) GetParams() Params { return Params{} } -func (m *GenesisState) GetDelegationsFS() types.GenesisState { - if m != nil { - return m.DelegationsFS - } - return types.GenesisState{} -} - -func (m *GenesisState) GetDelegatorsFS() types.GenesisState { +func (m *GenesisState) GetDelegatorRewardList() []DelegatorReward { if m != nil { - return m.DelegatorsFS + return m.DelegatorRewardList } - return types.GenesisState{} + return nil } -func (m *GenesisState) GetDelegatorRewardList() []DelegatorReward { +func (m *GenesisState) GetDelegations() []Delegation { if m != nil { - return m.DelegatorRewardList + return m.Delegations } return nil } @@ -103,28 +93,27 @@ func init() { } var fileDescriptor_d5bca863c53f218f = []byte{ - // 336 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x4f, 0x4b, 0xc3, 0x30, - 0x18, 0xc6, 0xdb, 0xfd, 0x43, 0xba, 0x09, 0x52, 0x15, 0xca, 0x0e, 0x75, 0x28, 0xca, 0x76, 0x49, - 0x60, 0xbb, 0x7b, 0x18, 0xa2, 0x22, 0x1e, 0x64, 0xf3, 0xe4, 0x65, 0x64, 0x6b, 0x8c, 0xc1, 0xae, - 0x19, 0xc9, 0xbb, 0x39, 0xbf, 0x85, 0x1f, 0x6b, 0xc7, 0x1d, 0x3d, 0x89, 0xac, 0x5f, 0x44, 0x9a, - 0xc6, 0xb1, 0x08, 0xbd, 0x78, 0x4a, 0x5a, 0x7e, 0xcf, 0x2f, 0x79, 0xf2, 0x7a, 0x17, 0x31, 0x59, - 0x90, 0x84, 0x02, 0xce, 0x56, 0x1c, 0xcd, 0x49, 0xac, 0x80, 0xbc, 0xf2, 0x84, 0x61, 0x46, 0x13, - 0xaa, 0xb8, 0x42, 0x33, 0x29, 0x40, 0xf8, 0x81, 0xe1, 0x50, 0xb6, 0xa2, 0x1d, 0xae, 0x79, 0xc4, - 0x04, 0x13, 0x1a, 0xc2, 0xd9, 0x2e, 0xe7, 0x9b, 0xe7, 0x85, 0xde, 0x19, 0x91, 0x64, 0x6a, 0xb4, - 0xcd, 0x8e, 0x85, 0x3d, 0xf3, 0x25, 0x01, 0x2e, 0x12, 0x05, 0x42, 0xd2, 0xed, 0x97, 0x41, 0xcf, - 0x2c, 0x14, 0xf8, 0x94, 0xca, 0x9c, 0xd3, 0x5b, 0x03, 0xe1, 0xc2, 0x63, 0x23, 0x1a, 0x53, 0x46, - 0x40, 0xc8, 0x91, 0xa4, 0x6f, 0x44, 0x46, 0x79, 0xe0, 0x34, 0x2d, 0x79, 0x8d, 0x9b, 0xbc, 0xe9, - 0x10, 0x08, 0x50, 0xff, 0xd2, 0xab, 0xe5, 0x37, 0x0c, 0xdc, 0x96, 0xdb, 0xae, 0x77, 0x5b, 0xa8, - 0xa8, 0x39, 0x7a, 0xd0, 0x5c, 0xbf, 0xb2, 0xfa, 0x3a, 0x71, 0x06, 0x26, 0xe5, 0x3f, 0x7a, 0xfb, - 0xe6, 0xa8, 0xac, 0xc8, 0xf5, 0x30, 0x28, 0x69, 0x4d, 0xdb, 0xd6, 0x58, 0x4d, 0xd1, 0xee, 0x05, - 0x8c, 0xce, 0x96, 0xf8, 0x03, 0xaf, 0xb1, 0x2d, 0x90, 0x49, 0xcb, 0xff, 0x92, 0x5a, 0x0e, 0x7f, - 0xe2, 0x1d, 0xff, 0x7d, 0x94, 0x51, 0xcc, 0x15, 0x04, 0xd5, 0x56, 0xb9, 0x5d, 0xef, 0x76, 0x8a, - 0x8b, 0x5f, 0xfd, 0xc6, 0x06, 0x3a, 0x65, 0xec, 0x87, 0x91, 0xfd, 0xfb, 0x9e, 0x2b, 0xb8, 0xab, - 0xec, 0x55, 0x0e, 0xaa, 0xfd, 0xdb, 0xd5, 0x26, 0x74, 0xd7, 0x9b, 0xd0, 0xfd, 0xde, 0x84, 0xee, - 0x47, 0x1a, 0x3a, 0xeb, 0x34, 0x74, 0x3e, 0xd3, 0xd0, 0x79, 0x42, 0x8c, 0xc3, 0xcb, 0x7c, 0x8c, - 0x26, 0x62, 0x6a, 0xcf, 0x6e, 0xd1, 0xc3, 0x4b, 0x6b, 0x80, 0xf0, 0x3e, 0xa3, 0x6a, 0x5c, 0xd3, - 0x63, 0xeb, 0xfd, 0x04, 0x00, 0x00, 0xff, 0xff, 0x08, 0x1d, 0xa0, 0x30, 0xb8, 0x02, 0x00, 0x00, + // 308 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcb, 0x49, 0x2c, 0x4b, + 0xcc, 0x4b, 0x2d, 0xd1, 0x07, 0xd1, 0xfa, 0x29, 0xa5, 0x89, 0x39, 0xc5, 0x25, 0x89, 0xd9, 0x99, + 0x79, 0xe9, 0xfa, 0xe9, 0xa9, 0x79, 0xa9, 0xc5, 0x99, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, + 0x42, 0x12, 0x50, 0x75, 0x7a, 0x20, 0x5a, 0x0f, 0x49, 0x9d, 0x94, 0x48, 0x7a, 0x7e, 0x7a, 0x3e, + 0x58, 0x91, 0x3e, 0x88, 0x05, 0x51, 0x2f, 0xa5, 0x8a, 0xd3, 0xdc, 0x82, 0xc4, 0xa2, 0xc4, 0x5c, + 0xa8, 0xb1, 0x52, 0xfa, 0x38, 0x95, 0xa5, 0xa4, 0xe6, 0xa4, 0xa6, 0x27, 0x96, 0xe4, 0x17, 0xc5, + 0x17, 0xa5, 0x96, 0x27, 0x16, 0xa5, 0x40, 0x35, 0xa8, 0x13, 0xd2, 0x90, 0x0a, 0x51, 0xa8, 0x34, + 0x99, 0x89, 0x8b, 0xc7, 0x1d, 0xe2, 0x85, 0xe0, 0x92, 0xc4, 0x92, 0x54, 0x21, 0x3b, 0x2e, 0x36, + 0x88, 0xd5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xdc, 0x46, 0x0a, 0x7a, 0xb8, 0xbc, 0xa4, 0x17, 0x00, + 0x56, 0xe7, 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x54, 0x97, 0x50, 0x32, 0x97, 0x28, 0xba, + 0x9b, 0xe2, 0x73, 0x32, 0x8b, 0x4b, 0x24, 0x58, 0x15, 0x98, 0x35, 0xb8, 0x8d, 0x34, 0x71, 0x1b, + 0xe7, 0x02, 0xd3, 0x16, 0x04, 0xd6, 0x05, 0x35, 0x57, 0x38, 0x05, 0x55, 0xd8, 0x27, 0xb3, 0xb8, + 0x44, 0xc8, 0x87, 0x8b, 0x1b, 0xaa, 0x3a, 0x33, 0x3f, 0xaf, 0x58, 0x82, 0x0d, 0x6c, 0xb4, 0x0a, + 0x41, 0xa3, 0x33, 0xf3, 0xf3, 0xa0, 0xa6, 0x22, 0x6b, 0xf7, 0x62, 0xe1, 0x60, 0x12, 0x60, 0xf6, + 0x62, 0xe1, 0x60, 0x16, 0x60, 0xf1, 0x62, 0xe1, 0x60, 0x11, 0x60, 0x75, 0xf2, 0x38, 0xf1, 0x48, + 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, + 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xbd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, + 0xe4, 0xfc, 0x5c, 0xd4, 0x48, 0x29, 0x33, 0xd6, 0xaf, 0x40, 0x09, 0xe8, 0x92, 0xca, 0x82, 0xd4, + 0xe2, 0x24, 0x36, 0x70, 0x30, 0x1b, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x19, 0x97, 0xa1, 0xc1, + 0x41, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -147,6 +136,20 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Delegations) > 0 { + for iNdEx := len(m.Delegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Delegations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } if len(m.DelegatorRewardList) > 0 { for iNdEx := len(m.DelegatorRewardList) - 1; iNdEx >= 0; iNdEx-- { { @@ -161,26 +164,6 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x2a } } - { - size, err := m.DelegatorsFS.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - { - size, err := m.DelegationsFS.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 { size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -213,16 +196,18 @@ func (m *GenesisState) Size() (n int) { _ = l l = m.Params.Size() n += 1 + l + sovGenesis(uint64(l)) - l = m.DelegationsFS.Size() - n += 1 + l + sovGenesis(uint64(l)) - l = m.DelegatorsFS.Size() - n += 1 + l + sovGenesis(uint64(l)) if len(m.DelegatorRewardList) > 0 { for _, e := range m.DelegatorRewardList { l = e.Size() n += 1 + l + sovGenesis(uint64(l)) } } + if len(m.Delegations) > 0 { + for _, e := range m.Delegations { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } return n } @@ -294,42 +279,9 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DelegationsFS", 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.DelegationsFS.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DelegatorsFS", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorRewardList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -356,13 +308,14 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.DelegatorsFS.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.DelegatorRewardList = append(m.DelegatorRewardList, DelegatorReward{}) + if err := m.DelegatorRewardList[len(m.DelegatorRewardList)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 5: + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DelegatorRewardList", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Delegations", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -389,8 +342,8 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.DelegatorRewardList = append(m.DelegatorRewardList, DelegatorReward{}) - if err := m.DelegatorRewardList[len(m.DelegatorRewardList)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Delegations = append(m.Delegations, Delegation{}) + if err := m.Delegations[len(m.Delegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/dualstaking/types/genesis_test.go b/x/dualstaking/types/genesis_test.go index 48751c24af..aaa1b556a9 100644 --- a/x/dualstaking/types/genesis_test.go +++ b/x/dualstaking/types/genesis_test.go @@ -25,12 +25,10 @@ func TestGenesisState_Validate(t *testing.T) { { Provider: "p0", Delegator: "d0", - ChainId: "c0", }, { Provider: "p1", Delegator: "d1", - ChainId: "c1", }, }, // this line is used by starport scaffolding # types/genesis/validField @@ -44,12 +42,10 @@ func TestGenesisState_Validate(t *testing.T) { { Provider: "p0", Delegator: "d0", - ChainId: "c0", }, { Provider: "p0", Delegator: "d0", - ChainId: "c0", }, }, }, diff --git a/x/dualstaking/types/key_delegator_reward.go b/x/dualstaking/types/key_delegator_reward.go deleted file mode 100644 index 7bf7db8cb7..0000000000 --- a/x/dualstaking/types/key_delegator_reward.go +++ /dev/null @@ -1,23 +0,0 @@ -package types - -import "encoding/binary" - -var _ binary.ByteOrder - -const ( - // DelegatorRewardKeyPrefix is the prefix to retrieve all DelegatorReward - DelegatorRewardKeyPrefix = "DelegatorReward/value/" -) - -// DelegatorRewardKey returns the store key to retrieve a DelegatorReward from the index fields -func DelegatorRewardKey( - index string, -) []byte { - var key []byte - - indexBytes := []byte(index) - key = append(key, indexBytes...) - key = append(key, []byte("/")...) - - return key -} diff --git a/x/dualstaking/types/keys.go b/x/dualstaking/types/keys.go index b8aaa80a3e..73c73a8849 100644 --- a/x/dualstaking/types/keys.go +++ b/x/dualstaking/types/keys.go @@ -1,7 +1,7 @@ package types import ( - "strings" + "cosmossdk.io/collections" ) const ( @@ -17,15 +17,6 @@ const ( // MemStoreKey defines the in-memory store key MemStoreKey = "mem_dualstaking" - // prefix for the delegations fixation store - DelegationPrefix = "delegation-fs" - - // prefix for the delegators fixation store - DelegatorPrefix = "delegator-fs" - - // prefix for the unbonding timer store - UnbondingPrefix = "unbonding-ts" - // DisableDualstakingHooks prefix DisableDualstakingHookPrefix = "disable-dualstaking-hooks" @@ -37,24 +28,11 @@ func KeyPrefix(p string) []byte { return []byte(p) } -// DelegationKey returns the key/prefix for the Delegation entry in fixation store. -// Using " " (space) as spearator is safe because Bech32 forbids its use as part of -// the address (and is the only visible character that can be safely used). -// (reference https://en.bitcoin.it/wiki/BIP_0173#Specification) -func DelegationKey(provider, delegator, chainID string) string { - return provider + " " + delegator + " " + chainID -} - -func DelegationKeyDecode(prefix string) (provider, delegator, chainID string) { - split := strings.Split(prefix, " ") - return split[0], split[1], split[2] +func DelegationKey(provider, delegator string) collections.Pair[string, string] { + return collections.Join(provider, delegator) } -// DelegatorKey returns the key/prefix for the Delegator entry in fixation store. -func DelegatorKey(delegator string) string { - return delegator -} - -func DelegatorKeyDecode(prefix string) (delegator string) { - return prefix -} +var ( + DelegationsPrefix = collections.NewPrefix([]byte("Delegations/")) + RewardPrefix = collections.NewPrefix([]byte("Rewards/")) +) diff --git a/x/dualstaking/types/message_delegate_test.go b/x/dualstaking/types/message_delegate_test.go index c58d392d9d..5b8916fcc1 100644 --- a/x/dualstaking/types/message_delegate_test.go +++ b/x/dualstaking/types/message_delegate_test.go @@ -6,7 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" legacyerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/lavanet/lava/v3/testutil/sample" - commontypes "github.com/lavanet/lava/v3/utils/common/types" "github.com/stretchr/testify/require" ) @@ -26,7 +25,7 @@ func TestMsgDelegate_ValidateBasic(t *testing.T) { Provider: sample.AccAddress(), Amount: oneCoin, Validator: validator, - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, err: legacyerrors.ErrInvalidAddress, }, @@ -37,7 +36,7 @@ func TestMsgDelegate_ValidateBasic(t *testing.T) { Provider: "invalid_address", Amount: oneCoin, Validator: validator, - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, err: legacyerrors.ErrInvalidAddress, }, @@ -48,7 +47,7 @@ func TestMsgDelegate_ValidateBasic(t *testing.T) { Provider: sample.AccAddress(), Amount: oneCoin, Validator: validator, - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, }, { @@ -58,7 +57,7 @@ func TestMsgDelegate_ValidateBasic(t *testing.T) { Provider: sample.AccAddress(), Amount: sdk.Coin{Denom: "utest", Amount: sdk.NewInt(-1)}, Validator: validator, - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, err: legacyerrors.ErrInvalidCoins, }, @@ -69,7 +68,7 @@ func TestMsgDelegate_ValidateBasic(t *testing.T) { Provider: sample.AccAddress(), Amount: oneCoin, Validator: "invalid_validator", - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, err: legacyerrors.ErrInvalidAddress, }, diff --git a/x/dualstaking/types/message_redelegate_test.go b/x/dualstaking/types/message_redelegate_test.go index 164f97457d..d3c2e3e79f 100644 --- a/x/dualstaking/types/message_redelegate_test.go +++ b/x/dualstaking/types/message_redelegate_test.go @@ -6,7 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" legacyerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/lavanet/lava/v3/testutil/sample" - commontypes "github.com/lavanet/lava/v3/utils/common/types" "github.com/stretchr/testify/require" ) @@ -25,8 +24,8 @@ func TestMsgRedelegate_ValidateBasic(t *testing.T) { FromProvider: sample.AccAddress(), ToProvider: sample.AccAddress(), Amount: oneCoin, - FromChainID: commontypes.EMPTY_PROVIDER_CHAINID, - ToChainID: commontypes.EMPTY_PROVIDER_CHAINID, + FromChainID: "", + ToChainID: "", }, err: legacyerrors.ErrInvalidAddress, }, { @@ -36,8 +35,8 @@ func TestMsgRedelegate_ValidateBasic(t *testing.T) { FromProvider: "invalid_address", ToProvider: sample.AccAddress(), Amount: oneCoin, - FromChainID: commontypes.EMPTY_PROVIDER_CHAINID, - ToChainID: commontypes.EMPTY_PROVIDER_CHAINID, + FromChainID: "", + ToChainID: "", }, err: legacyerrors.ErrInvalidAddress, }, { @@ -47,8 +46,8 @@ func TestMsgRedelegate_ValidateBasic(t *testing.T) { FromProvider: sample.AccAddress(), ToProvider: sample.AccAddress(), Amount: oneCoin, - FromChainID: commontypes.EMPTY_PROVIDER_CHAINID, - ToChainID: commontypes.EMPTY_PROVIDER_CHAINID, + FromChainID: "", + ToChainID: "", }, }, { name: "invalid amount", @@ -57,8 +56,8 @@ func TestMsgRedelegate_ValidateBasic(t *testing.T) { FromProvider: sample.AccAddress(), ToProvider: sample.AccAddress(), Amount: sdk.Coin{Denom: "utest", Amount: sdk.NewInt(-1)}, - FromChainID: commontypes.EMPTY_PROVIDER_CHAINID, - ToChainID: commontypes.EMPTY_PROVIDER_CHAINID, + FromChainID: "", + ToChainID: "", }, err: legacyerrors.ErrInvalidCoins, }, diff --git a/x/dualstaking/types/message_unbond_test.go b/x/dualstaking/types/message_unbond_test.go index b18ba40cce..08bcf0dab0 100644 --- a/x/dualstaking/types/message_unbond_test.go +++ b/x/dualstaking/types/message_unbond_test.go @@ -6,7 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" legacyerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/lavanet/lava/v3/testutil/sample" - commontypes "github.com/lavanet/lava/v3/utils/common/types" "github.com/stretchr/testify/require" ) @@ -26,7 +25,7 @@ func TestMsgUnbond_ValidateBasic(t *testing.T) { Provider: sample.AccAddress(), Amount: oneCoin, Validator: validator, - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, err: legacyerrors.ErrInvalidAddress, }, @@ -37,7 +36,7 @@ func TestMsgUnbond_ValidateBasic(t *testing.T) { Provider: "invalid_address", Amount: oneCoin, Validator: validator, - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, err: legacyerrors.ErrInvalidAddress, }, @@ -48,7 +47,7 @@ func TestMsgUnbond_ValidateBasic(t *testing.T) { Provider: sample.AccAddress(), Amount: oneCoin, Validator: "invalid_validator", - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, err: legacyerrors.ErrInvalidAddress, }, @@ -59,7 +58,7 @@ func TestMsgUnbond_ValidateBasic(t *testing.T) { Provider: sample.AccAddress(), Amount: oneCoin, Validator: validator, - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, }, { @@ -69,7 +68,7 @@ func TestMsgUnbond_ValidateBasic(t *testing.T) { Provider: sample.AccAddress(), Amount: sdk.Coin{Denom: "utest", Amount: sdk.NewInt(-1)}, Validator: validator, - ChainID: commontypes.EMPTY_PROVIDER_CHAINID, + ChainID: "", }, err: legacyerrors.ErrInvalidCoins, }, diff --git a/x/dualstaking/types/query.pb.go b/x/dualstaking/types/query.pb.go index e572f9b124..ebdfedebcc 100644 --- a/x/dualstaking/types/query.pb.go +++ b/x/dualstaking/types/query.pb.go @@ -413,7 +413,6 @@ func (m *QueryDelegatorRewardsResponse) GetRewards() []DelegatorRewardInfo { type DelegatorRewardInfo struct { Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` } @@ -457,13 +456,6 @@ func (m *DelegatorRewardInfo) GetProvider() string { return "" } -func (m *DelegatorRewardInfo) GetChainId() string { - if m != nil { - return m.ChainId - } - return "" -} - func (m *DelegatorRewardInfo) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { if m != nil { return m.Amount @@ -488,53 +480,52 @@ func init() { } var fileDescriptor_8393eed0cfbc46b2 = []byte{ - // 722 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x95, 0x4f, 0x4f, 0x13, 0x4f, - 0x18, 0xc7, 0x3b, 0xe5, 0xf7, 0x2b, 0x30, 0xf5, 0x60, 0x06, 0x0e, 0xa5, 0xc1, 0xa5, 0x6e, 0x30, - 0x36, 0x46, 0x76, 0x04, 0x12, 0x01, 0x4d, 0xfc, 0x83, 0x1c, 0x24, 0x81, 0x88, 0x4d, 0xb8, 0x78, - 0x69, 0xa6, 0xdd, 0x71, 0x99, 0xd0, 0xce, 0x2c, 0x3b, 0xd3, 0x22, 0x21, 0x5c, 0x7c, 0x03, 0x9a, - 0xf8, 0x2e, 0xbc, 0xf9, 0x2e, 0x48, 0xf4, 0x40, 0xe2, 0xc5, 0x78, 0x40, 0x03, 0xbe, 0x10, 0xb3, - 0xb3, 0xb3, 0x75, 0x4b, 0x59, 0x5a, 0x48, 0xf4, 0xb4, 0xf4, 0x99, 0xe7, 0x79, 0xbe, 0xcf, 0x67, - 0x9e, 0x7c, 0x07, 0x38, 0xdd, 0x20, 0x6d, 0xc2, 0xa9, 0xc2, 0xe1, 0x17, 0xbb, 0x2d, 0xd2, 0x90, - 0x8a, 0x6c, 0x33, 0xee, 0xe1, 0x9d, 0x16, 0x0d, 0xf6, 0x1c, 0x3f, 0x10, 0x4a, 0xa0, 0x82, 0xc9, - 0x72, 0xc2, 0xaf, 0x93, 0xc8, 0x2a, 0x8e, 0x7b, 0xc2, 0x13, 0x3a, 0x09, 0x87, 0x7f, 0x45, 0xf9, - 0xc5, 0x49, 0x4f, 0x08, 0xaf, 0x41, 0x31, 0xf1, 0x19, 0x26, 0x9c, 0x0b, 0x45, 0x14, 0x13, 0x5c, - 0x9a, 0xd3, 0x3b, 0x75, 0x21, 0x9b, 0x42, 0xe2, 0x1a, 0x91, 0x34, 0x92, 0xc1, 0xed, 0xd9, 0x1a, - 0x55, 0x64, 0x16, 0xfb, 0xc4, 0x63, 0x5c, 0x27, 0x9b, 0xdc, 0x5b, 0xa9, 0xf3, 0xf9, 0x24, 0x20, - 0xcd, 0xb8, 0xe5, 0xed, 0xd4, 0x34, 0x97, 0x36, 0xa8, 0x47, 0x14, 0x35, 0x89, 0x56, 0x52, 0x3b, - 0x56, 0xad, 0x0b, 0x66, 0xf4, 0xec, 0x71, 0x88, 0x5e, 0x86, 0x13, 0x6d, 0xe8, 0xee, 0x15, 0xba, - 0xd3, 0xa2, 0x52, 0xd9, 0x9b, 0x70, 0xac, 0x2b, 0x2a, 0x7d, 0xc1, 0x25, 0x45, 0x8f, 0x60, 0x2e, - 0x9a, 0xa2, 0x00, 0x4a, 0xa0, 0x9c, 0x9f, 0x2b, 0x39, 0x69, 0xf7, 0xe4, 0x44, 0x95, 0xcb, 0xff, - 0x1d, 0x1e, 0x4f, 0x65, 0x2a, 0xa6, 0xca, 0x26, 0xd0, 0xd2, 0x6d, 0x57, 0xa2, 0x19, 0x45, 0xb0, - 0x11, 0x88, 0x36, 0x73, 0x69, 0x10, 0x0b, 0xa3, 0x49, 0x38, 0xea, 0xc6, 0x87, 0x5a, 0x64, 0xb4, - 0xf2, 0x27, 0x80, 0x6e, 0xc2, 0x6b, 0xbb, 0x4c, 0x6d, 0x55, 0x7d, 0xca, 0x5d, 0xc6, 0xbd, 0x42, - 0xb6, 0x04, 0xca, 0x23, 0x95, 0x7c, 0x18, 0xdb, 0x88, 0x42, 0xb6, 0x80, 0x53, 0xa9, 0x12, 0x86, - 0x62, 0x0d, 0xe6, 0x4d, 0xcb, 0x70, 0x47, 0x05, 0x50, 0x1a, 0x2a, 0xe7, 0xe7, 0xa6, 0xd3, 0x51, - 0x56, 0x3a, 0xc9, 0x06, 0x27, 0x59, 0x6e, 0x57, 0x0d, 0x53, 0xac, 0xd3, 0x11, 0xee, 0x30, 0x15, - 0xe1, 0x88, 0x6f, 0x0e, 0x0d, 0x52, 0xe7, 0xf7, 0x65, 0x88, 0xce, 0x13, 0xf8, 0x2b, 0x44, 0x12, - 0x4e, 0x76, 0x5f, 0x61, 0x85, 0xee, 0x92, 0xc0, 0x1d, 0x70, 0x47, 0x49, 0xda, 0xec, 0x19, 0xda, - 0x09, 0x38, 0x52, 0xdf, 0x22, 0x8c, 0x57, 0x99, 0x5b, 0x18, 0xd2, 0x67, 0xc3, 0xfa, 0xf7, 0xaa, - 0x6b, 0x73, 0x78, 0x23, 0x45, 0xd4, 0x30, 0xae, 0xc3, 0xe1, 0x20, 0x0a, 0x19, 0xbe, 0x99, 0xbe, - 0x7c, 0x71, 0x93, 0x55, 0xfe, 0x5a, 0x18, 0xd0, 0xb8, 0x87, 0xfd, 0x09, 0xc0, 0xb1, 0x73, 0xd2, - 0x2e, 0x5c, 0x56, 0x72, 0xfc, 0x6c, 0xd7, 0xf8, 0xa8, 0x0e, 0x73, 0xa4, 0x29, 0x5a, 0x5c, 0x15, - 0x86, 0xf4, 0x70, 0x13, 0x4e, 0xe4, 0x3b, 0x27, 0xf4, 0x9d, 0x63, 0x7c, 0xe7, 0x3c, 0x13, 0x8c, - 0x2f, 0xdf, 0x0b, 0x07, 0xf9, 0xf8, 0x63, 0xaa, 0xec, 0x31, 0xb5, 0xd5, 0xaa, 0x39, 0x75, 0xd1, - 0xc4, 0xc6, 0xa4, 0xd1, 0x67, 0x46, 0xba, 0xdb, 0x58, 0xed, 0xf9, 0x54, 0xea, 0x02, 0x59, 0x31, - 0xad, 0xe7, 0x8e, 0x87, 0xe1, 0xff, 0xfa, 0x92, 0xd0, 0x3b, 0x00, 0x73, 0x91, 0xc3, 0xd0, 0xdd, - 0xf4, 0x6b, 0xe8, 0x35, 0x76, 0x71, 0x66, 0xc0, 0xec, 0xe8, 0xd2, 0xed, 0xf2, 0xdb, 0xaf, 0xbf, - 0x3e, 0x64, 0x6d, 0x54, 0xc2, 0x7d, 0x9e, 0x25, 0xf4, 0x05, 0x40, 0xd4, 0xeb, 0x39, 0xb4, 0xd8, - 0x47, 0x2f, 0xf5, 0x25, 0x28, 0x2e, 0x5d, 0xa1, 0xd2, 0x4c, 0xfd, 0x54, 0x4f, 0xfd, 0x10, 0x2d, - 0xe1, 0x7e, 0xaf, 0xa4, 0x08, 0xaa, 0xf1, 0x76, 0x25, 0xde, 0xef, 0x04, 0x0f, 0xd0, 0x67, 0x00, - 0x51, 0xaf, 0xe1, 0xfa, 0xe2, 0xa4, 0x3e, 0x02, 0x7d, 0x71, 0xd2, 0xdd, 0x6d, 0x3f, 0xd1, 0x38, - 0x0f, 0xd0, 0xe2, 0x05, 0x4b, 0x30, 0xd5, 0xd5, 0x0e, 0x82, 0xc4, 0xfb, 0x71, 0xf0, 0x00, 0x7d, - 0x07, 0xf0, 0xfa, 0x59, 0x63, 0xa1, 0xfb, 0x83, 0x5e, 0x70, 0xb7, 0xfd, 0x8b, 0x0b, 0x97, 0xae, - 0x33, 0x1c, 0x9b, 0x9a, 0xe3, 0x05, 0x5a, 0x1f, 0x64, 0x2d, 0xc6, 0xa7, 0xc9, 0xa5, 0x24, 0x88, - 0xf0, 0x7e, 0x6c, 0xc4, 0x03, 0x74, 0x08, 0xe0, 0xf8, 0x59, 0xcd, 0x35, 0x26, 0xd5, 0xbf, 0x07, - 0x7c, 0xac, 0x01, 0x97, 0xd0, 0xc2, 0x15, 0x01, 0x97, 0x9f, 0x1f, 0x9e, 0x58, 0xe0, 0xe8, 0xc4, - 0x02, 0x3f, 0x4f, 0x2c, 0xf0, 0xfe, 0xd4, 0xca, 0x1c, 0x9d, 0x5a, 0x99, 0x6f, 0xa7, 0x56, 0xe6, - 0x95, 0x93, 0x78, 0x2c, 0xba, 0x9a, 0xb7, 0xe7, 0xf1, 0x9b, 0x2e, 0x05, 0xfd, 0x70, 0xd4, 0x72, - 0xfa, 0xbf, 0xfb, 0xfc, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0c, 0x54, 0x32, 0x8e, 0xef, 0x08, - 0x00, 0x00, + // 719 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x95, 0xcf, 0x4f, 0x13, 0x41, + 0x14, 0xc7, 0xbb, 0x2d, 0x96, 0x32, 0xf5, 0x40, 0x06, 0x0e, 0xa5, 0xc1, 0xa5, 0x6e, 0x30, 0x36, + 0x46, 0x76, 0x04, 0x12, 0x01, 0x4d, 0xfc, 0x81, 0x1c, 0xc4, 0x40, 0xc4, 0x26, 0x5c, 0xbc, 0x34, + 0xd3, 0xee, 0xb8, 0x4c, 0x68, 0x67, 0x96, 0x9d, 0x69, 0x91, 0x10, 0x2e, 0xfe, 0x03, 0x9a, 0x78, + 0xf5, 0x2f, 0xf0, 0x2f, 0x21, 0xd1, 0x03, 0x89, 0x17, 0xe3, 0x01, 0x0d, 0xf8, 0x87, 0x98, 0x9d, + 0x9d, 0xad, 0x5b, 0xca, 0xd2, 0x42, 0xa2, 0xa7, 0xa5, 0x6f, 0xde, 0x7b, 0xdf, 0xf7, 0x99, 0x97, + 0xef, 0x00, 0xa6, 0x1b, 0xb8, 0x8d, 0x19, 0x91, 0x28, 0xf8, 0x22, 0xa7, 0x85, 0x1b, 0x42, 0xe2, + 0x6d, 0xca, 0x5c, 0xb4, 0xd3, 0x22, 0xfe, 0x9e, 0xed, 0xf9, 0x5c, 0x72, 0x58, 0xd0, 0x59, 0x76, + 0xf0, 0xb5, 0x63, 0x59, 0xc5, 0x71, 0x97, 0xbb, 0x5c, 0x25, 0xa1, 0xe0, 0xaf, 0x30, 0xbf, 0x38, + 0xe9, 0x72, 0xee, 0x36, 0x08, 0xc2, 0x1e, 0x45, 0x98, 0x31, 0x2e, 0xb1, 0xa4, 0x9c, 0x09, 0x7d, + 0x7a, 0xa7, 0xce, 0x45, 0x93, 0x0b, 0x54, 0xc3, 0x82, 0x84, 0x32, 0xa8, 0x3d, 0x5b, 0x23, 0x12, + 0xcf, 0x22, 0x0f, 0xbb, 0x94, 0xa9, 0x64, 0x9d, 0x7b, 0x2b, 0x71, 0x3e, 0x0f, 0xfb, 0xb8, 0x19, + 0xb5, 0xbc, 0x9d, 0x98, 0xe6, 0x90, 0x06, 0x71, 0xb1, 0x24, 0x3a, 0xd1, 0x8c, 0x6b, 0x47, 0xaa, + 0x75, 0x4e, 0xb5, 0x9e, 0x35, 0x0e, 0xe0, 0xab, 0x60, 0xa2, 0x0d, 0xd5, 0xbd, 0x42, 0x76, 0x5a, + 0x44, 0x48, 0x6b, 0x13, 0x8c, 0x75, 0x45, 0x85, 0xc7, 0x99, 0x20, 0xf0, 0x11, 0xc8, 0x86, 0x53, + 0x14, 0x8c, 0x92, 0x51, 0xce, 0xcf, 0x95, 0xec, 0xa4, 0x7b, 0xb2, 0xc3, 0xca, 0xe5, 0xa1, 0xc3, + 0xe3, 0xa9, 0x54, 0x45, 0x57, 0x59, 0x18, 0x98, 0xaa, 0xed, 0x4a, 0x38, 0x23, 0xf7, 0x37, 0x7c, + 0xde, 0xa6, 0x0e, 0xf1, 0x23, 0x61, 0x38, 0x09, 0x46, 0x9c, 0xe8, 0x50, 0x89, 0x8c, 0x54, 0xfe, + 0x06, 0xe0, 0x4d, 0x70, 0x7d, 0x97, 0xca, 0xad, 0xaa, 0x47, 0x98, 0x43, 0x99, 0x5b, 0x48, 0x97, + 0x8c, 0x72, 0xae, 0x92, 0x0f, 0x62, 0x1b, 0x61, 0xc8, 0xe2, 0x60, 0x2a, 0x51, 0x42, 0x53, 0xac, + 0x81, 0xbc, 0x6e, 0x19, 0xec, 0xa8, 0x60, 0x94, 0x32, 0xe5, 0xfc, 0xdc, 0x74, 0x32, 0xca, 0x4a, + 0x27, 0x59, 0xe3, 0xc4, 0xcb, 0xad, 0xaa, 0x66, 0x8a, 0x74, 0x3a, 0xc2, 0x1d, 0xa6, 0x22, 0xc8, + 0x79, 0xfa, 0x50, 0x23, 0x75, 0x7e, 0x5f, 0x86, 0xe8, 0x3c, 0x81, 0x7f, 0x42, 0x24, 0xc0, 0x64, + 0xf7, 0x15, 0x56, 0xc8, 0x2e, 0xf6, 0x9d, 0x01, 0x77, 0x14, 0xa7, 0x4d, 0x9f, 0xa1, 0x9d, 0x00, + 0xb9, 0xfa, 0x16, 0xa6, 0xac, 0x4a, 0x9d, 0x42, 0x46, 0x9d, 0x0d, 0xab, 0xdf, 0xab, 0x8e, 0xc5, + 0xc0, 0x8d, 0x04, 0x51, 0xcd, 0xb8, 0x0e, 0x86, 0xfd, 0x30, 0xa4, 0xf9, 0x66, 0xfa, 0xf2, 0x45, + 0x4d, 0x56, 0xd9, 0x1b, 0xae, 0x41, 0xa3, 0x1e, 0xd6, 0x27, 0x03, 0x8c, 0x9d, 0x93, 0x76, 0xe1, + 0xb2, 0xea, 0x20, 0x8b, 0x9b, 0xbc, 0xc5, 0x64, 0x21, 0xa3, 0x26, 0x98, 0xb0, 0x43, 0x73, 0xd9, + 0x81, 0xb9, 0x6c, 0x6d, 0x2e, 0xfb, 0x19, 0xa7, 0x6c, 0xf9, 0x5e, 0xa0, 0xf6, 0xf9, 0xe7, 0x54, + 0xd9, 0xa5, 0x72, 0xab, 0x55, 0xb3, 0xeb, 0xbc, 0x89, 0xb4, 0x13, 0xc3, 0xcf, 0x8c, 0x70, 0xb6, + 0x91, 0xdc, 0xf3, 0x88, 0x50, 0x05, 0xa2, 0xa2, 0x5b, 0xbf, 0x18, 0xca, 0xa5, 0x47, 0x33, 0x73, + 0xc7, 0xc3, 0xe0, 0x9a, 0xba, 0x0f, 0xf8, 0xde, 0x00, 0xd9, 0xd0, 0x4c, 0xf0, 0x6e, 0x32, 0x71, + 0xaf, 0x87, 0x8b, 0x33, 0x03, 0x66, 0x87, 0xf7, 0x6b, 0x95, 0xdf, 0x7d, 0xfb, 0xfd, 0x31, 0x6d, + 0xc1, 0x12, 0xea, 0xf3, 0x02, 0xc1, 0xaf, 0x06, 0x80, 0xbd, 0xf6, 0x82, 0x8b, 0x7d, 0xf4, 0x12, + 0x4d, 0x5f, 0x5c, 0xba, 0x42, 0xa5, 0x9e, 0xfa, 0xa9, 0x9a, 0xfa, 0x21, 0x5c, 0x42, 0xfd, 0x1e, + 0x44, 0xee, 0x57, 0xa3, 0x45, 0x0a, 0xb4, 0xdf, 0x09, 0x1e, 0xc0, 0x2f, 0x06, 0x80, 0xbd, 0xde, + 0xea, 0x8b, 0x93, 0xe8, 0xf7, 0xbe, 0x38, 0xc9, 0x46, 0xb6, 0x9e, 0x28, 0x9c, 0x07, 0x70, 0xf1, + 0x82, 0x25, 0xe8, 0xea, 0x6a, 0x07, 0x41, 0xa0, 0xfd, 0x28, 0x78, 0x00, 0x7f, 0x18, 0x60, 0xf4, + 0xac, 0x87, 0xe0, 0xfd, 0x41, 0x2f, 0xb8, 0xdb, 0xe9, 0xc5, 0x85, 0x4b, 0xd7, 0x69, 0x8e, 0x4d, + 0xc5, 0xf1, 0x12, 0xae, 0x0f, 0xb2, 0x16, 0x6d, 0xc9, 0xf8, 0x52, 0x62, 0x44, 0x68, 0x3f, 0x7a, + 0x32, 0x0e, 0xe0, 0xa1, 0x01, 0xc6, 0xcf, 0x6a, 0xae, 0x51, 0x21, 0xff, 0x3f, 0xe0, 0x63, 0x05, + 0xb8, 0x04, 0x17, 0xae, 0x08, 0xb8, 0xfc, 0xfc, 0xf0, 0xc4, 0x34, 0x8e, 0x4e, 0x4c, 0xe3, 0xd7, + 0x89, 0x69, 0x7c, 0x38, 0x35, 0x53, 0x47, 0xa7, 0x66, 0xea, 0xfb, 0xa9, 0x99, 0x7a, 0x6d, 0xc7, + 0x9e, 0x8c, 0xae, 0xe6, 0xed, 0x79, 0xf4, 0xb6, 0x4b, 0x41, 0x3d, 0x1f, 0xb5, 0xac, 0xfa, 0x47, + 0x3e, 0xff, 0x27, 0x00, 0x00, 0xff, 0xff, 0x70, 0x47, 0xc0, 0x97, 0xda, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1096,13 +1087,6 @@ func (m *DelegatorRewardInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x1a } } - 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 - } if len(m.Provider) > 0 { i -= len(m.Provider) copy(dAtA[i:], m.Provider) @@ -1252,10 +1236,6 @@ func (m *DelegatorRewardInfo) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } - l = len(m.ChainId) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } if len(m.Amount) > 0 { for _, e := range m.Amount { l = e.Size() @@ -2067,38 +2047,6 @@ func (m *DelegatorRewardInfo) Unmarshal(dAtA []byte) error { } 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 Amount", wireType) diff --git a/x/epochstorage/README.md b/x/epochstorage/README.md index cab73a26ac..5a197c3c21 100644 --- a/x/epochstorage/README.md +++ b/x/epochstorage/README.md @@ -13,6 +13,7 @@ Note that the module will make sure that any changes will be applied only in the * [Epoch](#epoch) * [EpochDetails](#epochdetails) * [FixatedParams](#fixatedparams) + * [Metadata](#metadata) * [StakeEntry](#stakeentry) * [StakeEntry Storage](#stakeentry-storage) * [Parameters](#parameters) @@ -63,6 +64,21 @@ type FixatedParams struct { This is done in the [BeginBlock method of the module](keeper/fixated_params.go) +### Metadata + +The metadata struct includes all the data for a provider that is the same accross all chains +```go +type ProviderMetadata struct { + Provider string // provider address + Vault string // vault address + TotalDelegations types.Coin // total delegations by delegators + Chains []string // list of all chain ids the provider is staken on + DelegateCommission uint64 // provider commission from rewards + LastChange uint64 // date of the last commission change + Description types1.Description // string descriptions of the provider entity (moniker, identity, wesite...) +} +``` + ### StakeEntry The stake entry is a struct that contains all the information of a provider. @@ -146,6 +162,7 @@ The epochstorage module supports the following queries: | `show-fixated-params` | chainid | a specific fixated param | | `list-stake-storage` | chainid | list of all stake storages indices | | `show-stake-storage` | chainid | show a specific stake storage | +| `provider-metadata` | provider-address | shows the metadata of a specific provider, if left empty returns metadata for all providers | ## Transactions diff --git a/x/epochstorage/client/cli/query.go b/x/epochstorage/client/cli/query.go index decae416ca..1f0b91db88 100644 --- a/x/epochstorage/client/cli/query.go +++ b/x/epochstorage/client/cli/query.go @@ -30,6 +30,8 @@ func GetQueryCmd(queryRoute string) *cobra.Command { cmd.AddCommand(CmdShowEpochDetails()) cmd.AddCommand(CmdListFixatedParams()) cmd.AddCommand(CmdShowFixatedParams()) + cmd.AddCommand(CmdProviderMetadata()) + // this line is used by starport scaffolding # 1 return cmd diff --git a/x/epochstorage/client/cli/query_provider_metadata.go b/x/epochstorage/client/cli/query_provider_metadata.go new file mode 100644 index 0000000000..cf2a7bb355 --- /dev/null +++ b/x/epochstorage/client/cli/query_provider_metadata.go @@ -0,0 +1,44 @@ +package cli + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/lavanet/lava/v3/x/epochstorage/types" + "github.com/spf13/cobra" +) + +func CmdProviderMetadata() *cobra.Command { + cmd := &cobra.Command{ + Use: "provider-metadata", + Short: "returns the metadata for the provider address, if empty returns the metadata of all providers", + Example: "lavad provider-metadata lava@12345", + Args: cobra.RangeArgs(0, 1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + provider := "" + if len(args) == 1 { + provider = args[0] + } + + res, err := queryClient.ProviderMetaData(context.Background(), &types.QueryProviderMetaDataRequest{Provider: provider}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddPaginationFlagsToCmd(cmd, cmd.Use) + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/epochstorage/keeper/grpc_query_provider_metadata.go b/x/epochstorage/keeper/grpc_query_provider_metadata.go new file mode 100644 index 0000000000..5587c2e2b9 --- /dev/null +++ b/x/epochstorage/keeper/grpc_query_provider_metadata.go @@ -0,0 +1,29 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v3/x/epochstorage/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (k Keeper) ProviderMetaData(c context.Context, req *types.QueryProviderMetaDataRequest) (*types.QueryProviderMetaDataResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(c) + var err error + res := types.QueryProviderMetaDataResponse{} + if req.Provider == "" { + res.MetaData, err = k.GetAllMetadata(ctx) + } else { + var metadata types.ProviderMetadata + metadata, err = k.GetMetadata(ctx, req.Provider) + res.MetaData = append(res.MetaData, metadata) + } + + return &res, err +} diff --git a/x/epochstorage/keeper/keeper.go b/x/epochstorage/keeper/keeper.go index 999f0932e5..a289969d11 100644 --- a/x/epochstorage/keeper/keeper.go +++ b/x/epochstorage/keeper/keeper.go @@ -34,6 +34,7 @@ type ( stakeEntries *collections.IndexedMap[collections.Triple[uint64, string, collections.Pair[uint64, string]], types.StakeEntry, types.EpochChainIdProviderIndexes] stakeEntriesCurrent *collections.IndexedMap[collections.Pair[string, string], types.StakeEntry, types.ChainIdVaultIndexes] epochHashes collections.Map[uint64, []byte] + providersMetaData collections.Map[string, types.ProviderMetadata] } ) @@ -77,6 +78,8 @@ func NewKeeper( collcompat.ProtoValue[types.StakeEntry](cdc), types.NewChainIdVaultIndexes(sb)), epochHashes: collections.NewMap(sb, types.EpochHashesPrefix, "epoch_hashes", collections.Uint64Key, collections.BytesValue), + + providersMetaData: collections.NewMap(sb, types.ProviderMetaDataPrefix, "provider_metadata", collections.StringKey, collcompat.ProtoValue[types.ProviderMetadata](cdc)), } keeper.AddFixationRegistry(string(types.KeyEpochBlocks), func(ctx sdk.Context) any { return keeper.EpochBlocksRaw(ctx) }) diff --git a/x/epochstorage/keeper/provider_metadata.go b/x/epochstorage/keeper/provider_metadata.go new file mode 100644 index 0000000000..9c3db359b3 --- /dev/null +++ b/x/epochstorage/keeper/provider_metadata.go @@ -0,0 +1,35 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v3/x/epochstorage/types" +) + +func (k Keeper) GetMetadata(ctx sdk.Context, provider string) (types.ProviderMetadata, error) { + return k.providersMetaData.Get(ctx, provider) +} + +func (k Keeper) SetMetadata(ctx sdk.Context, metadata types.ProviderMetadata) { + err := k.providersMetaData.Set(ctx, metadata.Provider, metadata) + if err != nil { + panic(err) + } +} + +func (k Keeper) RemoveMetadata(ctx sdk.Context, provider string) error { + return k.providersMetaData.Remove(ctx, provider) +} + +func (k Keeper) SetAllMetadata(ctx sdk.Context, metadata []types.ProviderMetadata) { + for _, md := range metadata { + k.SetMetadata(ctx, md) + } +} + +func (k Keeper) GetAllMetadata(ctx sdk.Context) ([]types.ProviderMetadata, error) { + iter, err := k.providersMetaData.Iterate(ctx, nil) + if err != nil { + panic(err) + } + return iter.Values() +} diff --git a/x/epochstorage/keeper/stake_entries.go b/x/epochstorage/keeper/stake_entries.go index de35d9d4d4..ad2abcec6e 100644 --- a/x/epochstorage/keeper/stake_entries.go +++ b/x/epochstorage/keeper/stake_entries.go @@ -8,6 +8,7 @@ import ( "cosmossdk.io/collections" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v3/utils" + "github.com/lavanet/lava/v3/utils/lavaslices" "github.com/lavanet/lava/v3/x/epochstorage/types" ) @@ -187,6 +188,23 @@ func (k Keeper) RemoveStakeEntryCurrent(ctx sdk.Context, chainID string, provide if err != nil { panic(fmt.Errorf("RemoveStakeEntryCurrent: Failed to remove entry with key %v, error: %w", key, err)) } + + metadata, err := k.GetMetadata(ctx, provider) + if err != nil { + panic(fmt.Errorf("RemoveStakeEntryCurrent: Failed to fetch provider metadata %v, error: %w", provider, err)) + } + var ok bool + metadata.Chains, ok = lavaslices.Remove(metadata.Chains, chainID) + if !ok { + panic(fmt.Errorf("RemoveStakeEntryCurrent: Failed to remove chain from provider metadata %v, error: %w", provider, err)) + } + if len(metadata.Chains) == 0 { + if k.RemoveMetadata(ctx, provider) != nil { + panic(fmt.Errorf("RemoveStakeEntryCurrent: Failed to remove provider metadata %v, error: %w", provider, err)) + } + } else { + k.SetMetadata(ctx, metadata) + } } // GetAllStakeEntriesCurrent gets all the current stake entries diff --git a/x/epochstorage/keeper/stake_entries_test.go b/x/epochstorage/keeper/stake_entries_test.go index 91d40c9420..0bdab37d08 100644 --- a/x/epochstorage/keeper/stake_entries_test.go +++ b/x/epochstorage/keeper/stake_entries_test.go @@ -99,6 +99,12 @@ func createNStakeEntriesCurrent(keeper *keeper.Keeper, ctx sdk.Context, n int) [ Chain: strconv.Itoa(i), } keeper.SetStakeEntryCurrent(ctx, items[i]) + metadata := types.ProviderMetadata{ + Provider: items[i].Address, + Vault: items[i].Vault, + Chains: []string{items[i].Chain}, + } + keeper.SetMetadata(ctx, metadata) } return items } diff --git a/x/epochstorage/types/provider_metadata.pb.go b/x/epochstorage/types/provider_metadata.pb.go new file mode 100644 index 0000000000..873bcdbcd8 --- /dev/null +++ b/x/epochstorage/types/provider_metadata.pb.go @@ -0,0 +1,618 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: lavanet/lava/epochstorage/provider_metadata.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + types1 "github.com/cosmos/cosmos-sdk/x/staking/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 + +type ProviderMetadata struct { + Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` + Vault string `protobuf:"bytes,2,opt,name=vault,proto3" json:"vault,omitempty"` + TotalDelegations types.Coin `protobuf:"bytes,3,opt,name=total_delegations,json=totalDelegations,proto3" json:"total_delegations"` + Chains []string `protobuf:"bytes,4,rep,name=chains,proto3" json:"chains,omitempty"` + DelegateCommission uint64 `protobuf:"varint,5,opt,name=delegate_commission,json=delegateCommission,proto3" json:"delegate_commission,omitempty"` + LastChange uint64 `protobuf:"varint,6,opt,name=last_change,json=lastChange,proto3" json:"last_change,omitempty"` + Description types1.Description `protobuf:"bytes,7,opt,name=description,proto3" json:"description"` +} + +func (m *ProviderMetadata) Reset() { *m = ProviderMetadata{} } +func (m *ProviderMetadata) String() string { return proto.CompactTextString(m) } +func (*ProviderMetadata) ProtoMessage() {} +func (*ProviderMetadata) Descriptor() ([]byte, []int) { + return fileDescriptor_eea88d8dbfad23fd, []int{0} +} +func (m *ProviderMetadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProviderMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProviderMetadata.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 *ProviderMetadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProviderMetadata.Merge(m, src) +} +func (m *ProviderMetadata) XXX_Size() int { + return m.Size() +} +func (m *ProviderMetadata) XXX_DiscardUnknown() { + xxx_messageInfo_ProviderMetadata.DiscardUnknown(m) +} + +var xxx_messageInfo_ProviderMetadata proto.InternalMessageInfo + +func (m *ProviderMetadata) GetProvider() string { + if m != nil { + return m.Provider + } + return "" +} + +func (m *ProviderMetadata) GetVault() string { + if m != nil { + return m.Vault + } + return "" +} + +func (m *ProviderMetadata) GetTotalDelegations() types.Coin { + if m != nil { + return m.TotalDelegations + } + return types.Coin{} +} + +func (m *ProviderMetadata) GetChains() []string { + if m != nil { + return m.Chains + } + return nil +} + +func (m *ProviderMetadata) GetDelegateCommission() uint64 { + if m != nil { + return m.DelegateCommission + } + return 0 +} + +func (m *ProviderMetadata) GetLastChange() uint64 { + if m != nil { + return m.LastChange + } + return 0 +} + +func (m *ProviderMetadata) GetDescription() types1.Description { + if m != nil { + return m.Description + } + return types1.Description{} +} + +func init() { + proto.RegisterType((*ProviderMetadata)(nil), "lavanet.lava.epochstorage.ProviderMetadata") +} + +func init() { + proto.RegisterFile("lavanet/lava/epochstorage/provider_metadata.proto", fileDescriptor_eea88d8dbfad23fd) +} + +var fileDescriptor_eea88d8dbfad23fd = []byte{ + // 401 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x92, 0x31, 0x6e, 0xdb, 0x30, + 0x18, 0x85, 0xcd, 0xc4, 0x71, 0x6b, 0x7a, 0x49, 0xd8, 0xa0, 0x50, 0x3c, 0x28, 0x46, 0xdb, 0xc1, + 0xe8, 0x40, 0xc2, 0xcd, 0x0d, 0xec, 0x2c, 0x05, 0x5a, 0x20, 0xd0, 0xd8, 0xc5, 0xf8, 0x45, 0x11, + 0x12, 0x51, 0x89, 0xbf, 0x20, 0x32, 0x42, 0x7b, 0x8b, 0x1e, 0xa3, 0x63, 0x8f, 0x91, 0xa9, 0xc8, + 0xd8, 0xa9, 0x28, 0xec, 0xa1, 0xd7, 0x08, 0x44, 0x51, 0x4e, 0xb2, 0x88, 0x7a, 0xff, 0x7b, 0x8f, + 0xfa, 0x20, 0x92, 0xae, 0x4a, 0x68, 0xc1, 0x28, 0x27, 0xba, 0x55, 0xa8, 0x1a, 0x65, 0x61, 0x1d, + 0x36, 0x90, 0x2b, 0x51, 0x37, 0xd8, 0xea, 0x4c, 0x35, 0xdb, 0x4a, 0x39, 0xc8, 0xc0, 0x01, 0xaf, + 0x1b, 0x74, 0xc8, 0x2e, 0x42, 0x85, 0x77, 0x2b, 0x7f, 0x5a, 0x99, 0x9f, 0xe7, 0x98, 0xa3, 0x4f, + 0x89, 0xee, 0xad, 0x2f, 0xcc, 0x63, 0x89, 0xb6, 0x42, 0x2b, 0x52, 0xb0, 0x4a, 0xb4, 0xab, 0x54, + 0x39, 0x58, 0x09, 0x89, 0xda, 0x04, 0xff, 0x0c, 0x2a, 0x6d, 0x50, 0xf8, 0x67, 0x18, 0xbd, 0x0b, + 0x15, 0xeb, 0xe0, 0xab, 0x36, 0xf9, 0xa1, 0x15, 0x74, 0x9f, 0x7a, 0xf3, 0xfb, 0x88, 0x9e, 0xde, + 0x04, 0xca, 0xcf, 0x01, 0x92, 0xcd, 0xe9, 0xcb, 0x81, 0x3c, 0x22, 0x0b, 0xb2, 0x9c, 0x26, 0x07, + 0xcd, 0xce, 0xe9, 0x49, 0x0b, 0xb7, 0xa5, 0x8b, 0x8e, 0xbc, 0xd1, 0x0b, 0xf6, 0x89, 0x9e, 0x39, + 0x74, 0x50, 0x6e, 0x33, 0x55, 0xaa, 0x1c, 0x9c, 0x46, 0x63, 0xa3, 0xe3, 0x05, 0x59, 0xce, 0x3e, + 0x5c, 0xf0, 0x1e, 0x84, 0x77, 0xec, 0x3c, 0x50, 0xf0, 0x0d, 0x6a, 0xb3, 0x1e, 0xdf, 0xfd, 0xbd, + 0x1c, 0x25, 0xa7, 0xbe, 0x79, 0xfd, 0x58, 0x64, 0xaf, 0xe9, 0x44, 0x16, 0xa0, 0x8d, 0x8d, 0xc6, + 0x8b, 0xe3, 0xe5, 0x34, 0x09, 0x8a, 0x09, 0xfa, 0x2a, 0xec, 0xaf, 0xb6, 0x12, 0xab, 0x4a, 0x5b, + 0xab, 0xd1, 0x44, 0x27, 0x0b, 0xb2, 0x1c, 0x27, 0x6c, 0xb0, 0x36, 0x07, 0x87, 0x5d, 0xd2, 0x59, + 0x09, 0xd6, 0x6d, 0x65, 0x01, 0x26, 0x57, 0xd1, 0xc4, 0x07, 0x69, 0x37, 0xda, 0xf8, 0x09, 0xbb, + 0xa1, 0xb3, 0x4c, 0x59, 0xd9, 0xe8, 0xba, 0xfb, 0x72, 0xf4, 0xc2, 0x13, 0xbf, 0x1d, 0x88, 0x87, + 0x5f, 0x35, 0x40, 0x5f, 0x3f, 0x46, 0xd7, 0xd3, 0x8e, 0xfd, 0xe7, 0xff, 0x5f, 0xef, 0x49, 0xf2, + 0x74, 0x8b, 0xf5, 0xc7, 0xbb, 0x5d, 0x4c, 0xee, 0x77, 0x31, 0xf9, 0xb7, 0x8b, 0xc9, 0x8f, 0x7d, + 0x3c, 0xba, 0xdf, 0xc7, 0xa3, 0x3f, 0xfb, 0x78, 0xf4, 0x45, 0xe4, 0xda, 0x15, 0xb7, 0x29, 0x97, + 0x58, 0x89, 0x67, 0x57, 0xa6, 0xbd, 0x12, 0xdf, 0x9e, 0xdf, 0x1b, 0xf7, 0xbd, 0x56, 0x36, 0x9d, + 0xf8, 0x23, 0xba, 0x7a, 0x08, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x00, 0x30, 0x06, 0x61, 0x02, 0x00, + 0x00, +} + +func (m *ProviderMetadata) 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 *ProviderMetadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProviderMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Description.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProviderMetadata(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + if m.LastChange != 0 { + i = encodeVarintProviderMetadata(dAtA, i, uint64(m.LastChange)) + i-- + dAtA[i] = 0x30 + } + if m.DelegateCommission != 0 { + i = encodeVarintProviderMetadata(dAtA, i, uint64(m.DelegateCommission)) + i-- + dAtA[i] = 0x28 + } + if len(m.Chains) > 0 { + for iNdEx := len(m.Chains) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Chains[iNdEx]) + copy(dAtA[i:], m.Chains[iNdEx]) + i = encodeVarintProviderMetadata(dAtA, i, uint64(len(m.Chains[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + { + size, err := m.TotalDelegations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProviderMetadata(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Vault) > 0 { + i -= len(m.Vault) + copy(dAtA[i:], m.Vault) + i = encodeVarintProviderMetadata(dAtA, i, uint64(len(m.Vault))) + i-- + dAtA[i] = 0x12 + } + if len(m.Provider) > 0 { + i -= len(m.Provider) + copy(dAtA[i:], m.Provider) + i = encodeVarintProviderMetadata(dAtA, i, uint64(len(m.Provider))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintProviderMetadata(dAtA []byte, offset int, v uint64) int { + offset -= sovProviderMetadata(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ProviderMetadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Provider) + if l > 0 { + n += 1 + l + sovProviderMetadata(uint64(l)) + } + l = len(m.Vault) + if l > 0 { + n += 1 + l + sovProviderMetadata(uint64(l)) + } + l = m.TotalDelegations.Size() + n += 1 + l + sovProviderMetadata(uint64(l)) + if len(m.Chains) > 0 { + for _, s := range m.Chains { + l = len(s) + n += 1 + l + sovProviderMetadata(uint64(l)) + } + } + if m.DelegateCommission != 0 { + n += 1 + sovProviderMetadata(uint64(m.DelegateCommission)) + } + if m.LastChange != 0 { + n += 1 + sovProviderMetadata(uint64(m.LastChange)) + } + l = m.Description.Size() + n += 1 + l + sovProviderMetadata(uint64(l)) + return n +} + +func sovProviderMetadata(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozProviderMetadata(x uint64) (n int) { + return sovProviderMetadata(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ProviderMetadata) 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 ErrIntOverflowProviderMetadata + } + 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: ProviderMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProviderMetadata: 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 ErrIntOverflowProviderMetadata + } + 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 ErrInvalidLengthProviderMetadata + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProviderMetadata + } + 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 Vault", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProviderMetadata + } + 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 ErrInvalidLengthProviderMetadata + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProviderMetadata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Vault = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalDelegations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProviderMetadata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProviderMetadata + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProviderMetadata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TotalDelegations.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Chains", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProviderMetadata + } + 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 ErrInvalidLengthProviderMetadata + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProviderMetadata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Chains = append(m.Chains, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegateCommission", wireType) + } + m.DelegateCommission = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProviderMetadata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DelegateCommission |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LastChange", wireType) + } + m.LastChange = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProviderMetadata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LastChange |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProviderMetadata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProviderMetadata + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProviderMetadata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Description.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProviderMetadata(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProviderMetadata + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipProviderMetadata(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, ErrIntOverflowProviderMetadata + } + 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, ErrIntOverflowProviderMetadata + } + 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, ErrIntOverflowProviderMetadata + } + 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, ErrInvalidLengthProviderMetadata + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupProviderMetadata + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthProviderMetadata + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthProviderMetadata = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowProviderMetadata = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupProviderMetadata = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/epochstorage/types/query.pb.go b/x/epochstorage/types/query.pb.go index 64e754ca79..2247632086 100644 --- a/x/epochstorage/types/query.pb.go +++ b/x/epochstorage/types/query.pb.go @@ -562,6 +562,94 @@ func (m *QueryAllFixatedParamsResponse) GetPagination() *query.PageResponse { return nil } +type QueryProviderMetaDataRequest struct { + Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` +} + +func (m *QueryProviderMetaDataRequest) Reset() { *m = QueryProviderMetaDataRequest{} } +func (m *QueryProviderMetaDataRequest) String() string { return proto.CompactTextString(m) } +func (*QueryProviderMetaDataRequest) ProtoMessage() {} +func (*QueryProviderMetaDataRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_60112e15fc266719, []int{12} +} +func (m *QueryProviderMetaDataRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProviderMetaDataRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProviderMetaDataRequest.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 *QueryProviderMetaDataRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProviderMetaDataRequest.Merge(m, src) +} +func (m *QueryProviderMetaDataRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryProviderMetaDataRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProviderMetaDataRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProviderMetaDataRequest proto.InternalMessageInfo + +func (m *QueryProviderMetaDataRequest) GetProvider() string { + if m != nil { + return m.Provider + } + return "" +} + +type QueryProviderMetaDataResponse struct { + MetaData []ProviderMetadata `protobuf:"bytes,1,rep,name=MetaData,proto3" json:"MetaData"` +} + +func (m *QueryProviderMetaDataResponse) Reset() { *m = QueryProviderMetaDataResponse{} } +func (m *QueryProviderMetaDataResponse) String() string { return proto.CompactTextString(m) } +func (*QueryProviderMetaDataResponse) ProtoMessage() {} +func (*QueryProviderMetaDataResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_60112e15fc266719, []int{13} +} +func (m *QueryProviderMetaDataResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProviderMetaDataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProviderMetaDataResponse.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 *QueryProviderMetaDataResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProviderMetaDataResponse.Merge(m, src) +} +func (m *QueryProviderMetaDataResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryProviderMetaDataResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProviderMetaDataResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProviderMetaDataResponse proto.InternalMessageInfo + +func (m *QueryProviderMetaDataResponse) GetMetaData() []ProviderMetadata { + if m != nil { + return m.MetaData + } + return nil +} + func init() { proto.RegisterType((*QueryParamsRequest)(nil), "lavanet.lava.epochstorage.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "lavanet.lava.epochstorage.QueryParamsResponse") @@ -575,6 +663,8 @@ func init() { proto.RegisterType((*QueryGetFixatedParamsResponse)(nil), "lavanet.lava.epochstorage.QueryGetFixatedParamsResponse") proto.RegisterType((*QueryAllFixatedParamsRequest)(nil), "lavanet.lava.epochstorage.QueryAllFixatedParamsRequest") proto.RegisterType((*QueryAllFixatedParamsResponse)(nil), "lavanet.lava.epochstorage.QueryAllFixatedParamsResponse") + proto.RegisterType((*QueryProviderMetaDataRequest)(nil), "lavanet.lava.epochstorage.QueryProviderMetaDataRequest") + proto.RegisterType((*QueryProviderMetaDataResponse)(nil), "lavanet.lava.epochstorage.QueryProviderMetaDataResponse") } func init() { @@ -582,51 +672,58 @@ func init() { } var fileDescriptor_60112e15fc266719 = []byte{ - // 703 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0x41, 0x4f, 0xd4, 0x40, - 0x14, 0xc7, 0x77, 0x40, 0x88, 0x8e, 0x10, 0xcd, 0xc8, 0x41, 0x57, 0xa8, 0x52, 0x23, 0x02, 0x86, - 0x19, 0x96, 0x35, 0x8a, 0x27, 0x03, 0x51, 0x88, 0x37, 0x58, 0x8c, 0x07, 0x2f, 0x64, 0x76, 0x19, - 0x4a, 0x63, 0xe9, 0x94, 0xed, 0x2c, 0x81, 0x18, 0x2f, 0x7e, 0x02, 0xa3, 0x9f, 0xc4, 0xc4, 0x83, - 0x9a, 0xc8, 0x99, 0x23, 0x89, 0x17, 0x4f, 0xc6, 0x80, 0x1f, 0xc4, 0x74, 0x66, 0xd6, 0xce, 0x84, - 0x96, 0x16, 0xe1, 0xb4, 0xdb, 0xe9, 0x7b, 0xff, 0xf7, 0xfb, 0xcf, 0xbc, 0xbe, 0x16, 0xde, 0x0d, - 0xe8, 0x36, 0x0d, 0x99, 0x20, 0xc9, 0x2f, 0x61, 0x11, 0x6f, 0x6d, 0xc4, 0x82, 0xb7, 0xa9, 0xc7, - 0xc8, 0x56, 0x87, 0xb5, 0x77, 0x71, 0xd4, 0xe6, 0x82, 0xa3, 0x1b, 0x3a, 0x0c, 0x27, 0xbf, 0xd8, - 0x0c, 0xab, 0x0e, 0x7b, 0x9c, 0x7b, 0x01, 0x23, 0x34, 0xf2, 0x09, 0x0d, 0x43, 0x2e, 0xa8, 0xf0, - 0x79, 0x18, 0xab, 0xc4, 0xea, 0x64, 0x8b, 0xc7, 0x9b, 0x3c, 0x26, 0x4d, 0x1a, 0x6b, 0x45, 0xb2, - 0x5d, 0x6b, 0x32, 0x41, 0x6b, 0x24, 0xa2, 0x9e, 0x1f, 0xca, 0x60, 0x1d, 0x3b, 0x96, 0xcf, 0x12, - 0xd1, 0x36, 0xdd, 0xec, 0x6a, 0x4e, 0xe5, 0xc7, 0xc5, 0x82, 0xbe, 0x66, 0xab, 0xfa, 0xaa, 0x38, - 0x5c, 0x5e, 0xac, 0xae, 0x31, 0x41, 0xfd, 0xa0, 0xab, 0x8e, 0xf3, 0xc3, 0xd7, 0xfd, 0x1d, 0x2a, - 0xd8, 0xda, 0xaa, 0x45, 0xe3, 0x98, 0x0e, 0xbb, 0xde, 0x5a, 0xdc, 0xef, 0xba, 0x1a, 0xf2, 0xb8, - 0xc7, 0xe5, 0x5f, 0x92, 0xfc, 0x53, 0xab, 0xee, 0x10, 0x44, 0xcb, 0xc9, 0x6e, 0x2c, 0x49, 0xa9, - 0x06, 0xdb, 0xea, 0xb0, 0x58, 0xb8, 0x2f, 0xe1, 0x35, 0x6b, 0x35, 0x8e, 0x78, 0x18, 0x33, 0xf4, - 0x04, 0xf6, 0xab, 0x92, 0xd7, 0xc1, 0x6d, 0x30, 0x7e, 0x79, 0x66, 0x14, 0xe7, 0x1e, 0x07, 0x56, - 0xa9, 0xf3, 0x17, 0xf6, 0x7f, 0xdd, 0xaa, 0x34, 0x74, 0x9a, 0x5b, 0x87, 0x37, 0xa5, 0xee, 0x22, - 0x13, 0x2b, 0xc9, 0x0e, 0xad, 0xa8, 0x60, 0x5d, 0x16, 0x0d, 0xc1, 0x3e, 0x3f, 0x5c, 0x63, 0x3b, - 0x52, 0xfe, 0x52, 0x43, 0x5d, 0xb8, 0x5b, 0x70, 0x38, 0x3b, 0x49, 0x53, 0x2d, 0xc3, 0x81, 0xd8, - 0x58, 0xd7, 0x6c, 0xf7, 0x4e, 0x60, 0x33, 0x65, 0x34, 0xa1, 0x25, 0xe1, 0x32, 0xcd, 0x39, 0x17, - 0x04, 0x59, 0x9c, 0x0b, 0x10, 0xa6, 0x4d, 0xa3, 0xeb, 0x8d, 0x61, 0xb5, 0xff, 0x38, 0xd9, 0x7f, - 0xac, 0x7a, 0x56, 0x9f, 0x02, 0x5e, 0x4a, 0x73, 0x1b, 0x46, 0xa6, 0xfb, 0x0d, 0x68, 0x6b, 0xc7, - 0xea, 0xe4, 0x5a, 0xeb, 0x3d, 0xa3, 0x35, 0xb4, 0x68, 0xb1, 0xf7, 0xe8, 0xbd, 0x2a, 0x62, 0x57, - 0x3c, 0x16, 0xfc, 0x48, 0x7a, 0x96, 0xcf, 0x12, 0x82, 0xa7, 0xaa, 0x7b, 0xbb, 0x2d, 0x64, 0x9c, - 0x9a, 0x7d, 0x3b, 0xb5, 0x66, 0xae, 0x97, 0x38, 0x35, 0x33, 0xbc, 0x6b, 0xcd, 0x5c, 0x73, 0x1f, - 0xa4, 0x25, 0x17, 0xd4, 0x13, 0x62, 0x75, 0x75, 0x4e, 0x7b, 0x75, 0xe0, 0x48, 0x4e, 0x96, 0x26, - 0x7d, 0x01, 0x07, 0xd7, 0xcd, 0x1b, 0x1a, 0x75, 0xfc, 0x04, 0x54, 0x4b, 0x48, 0xb3, 0xda, 0x22, - 0xee, 0x7a, 0x7a, 0xf4, 0x99, 0xb0, 0xe7, 0xd5, 0x63, 0x7b, 0x40, 0xfb, 0x3b, 0x5e, 0x28, 0xdf, - 0x5f, 0xef, 0x99, 0xfd, 0x9d, 0x5b, 0x9f, 0xcd, 0xec, 0x5d, 0x84, 0x7d, 0xd2, 0x00, 0xfa, 0x00, - 0x60, 0xbf, 0x56, 0x9f, 0x3a, 0x01, 0xee, 0xf8, 0x3c, 0xab, 0xe2, 0xb2, 0xe1, 0xaa, 0xbe, 0x3b, - 0xf1, 0xee, 0xc7, 0x9f, 0x8f, 0x3d, 0x77, 0xd0, 0x28, 0x29, 0x7a, 0x15, 0xa0, 0xaf, 0x00, 0x0e, - 0x98, 0x0f, 0x1d, 0x7a, 0x58, 0x54, 0x2b, 0x7b, 0xf8, 0x55, 0x1f, 0x9d, 0x3a, 0x4f, 0xc3, 0xce, - 0x4a, 0xd8, 0x19, 0x34, 0x4d, 0x4a, 0xbe, 0x8f, 0xc8, 0x1b, 0xd9, 0xf9, 0x6f, 0xd1, 0x67, 0x00, - 0xaf, 0x98, 0x92, 0x73, 0x41, 0x50, 0x8c, 0x9f, 0x3d, 0x13, 0x8b, 0xf1, 0x73, 0x66, 0x9c, 0x3b, - 0x2d, 0xf1, 0x27, 0xd1, 0x78, 0x59, 0x7c, 0xf4, 0x09, 0xd8, 0xb3, 0xa3, 0xd4, 0x96, 0x67, 0xcc, - 0xa8, 0x52, 0x5b, 0x9e, 0x35, 0xbc, 0x4a, 0x31, 0x5b, 0xef, 0x74, 0xf4, 0x1d, 0xc0, 0x41, 0xeb, - 0xa9, 0x41, 0x65, 0x8a, 0x67, 0x4d, 0x86, 0xea, 0xec, 0xe9, 0x13, 0x35, 0xf6, 0x63, 0x89, 0x5d, - 0x47, 0x35, 0x52, 0xf6, 0xdb, 0xe2, 0x5f, 0xab, 0x7c, 0x01, 0xf0, 0xaa, 0x25, 0x9a, 0xf4, 0x4a, - 0x99, 0x33, 0xff, 0x3f, 0x0b, 0x79, 0xc3, 0xca, 0xad, 0x49, 0x0b, 0xf7, 0xd1, 0x44, 0x69, 0x0b, - 0xf3, 0xcf, 0xf7, 0x0f, 0x1d, 0x70, 0x70, 0xe8, 0x80, 0xdf, 0x87, 0x0e, 0x78, 0x7f, 0xe4, 0x54, - 0x0e, 0x8e, 0x9c, 0xca, 0xcf, 0x23, 0xa7, 0xf2, 0x8a, 0x78, 0xbe, 0xd8, 0xe8, 0x34, 0x71, 0x8b, - 0x6f, 0xda, 0x72, 0xdb, 0x75, 0xb2, 0x63, 0x6b, 0x8a, 0xdd, 0x88, 0xc5, 0xcd, 0x7e, 0xf9, 0xd1, - 0x54, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xdb, 0x27, 0x7e, 0x6a, 0xae, 0x0a, 0x00, 0x00, + // 807 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xcd, 0x4f, 0x13, 0x4d, + 0x1c, 0xc7, 0x3b, 0xf0, 0x40, 0x78, 0xe6, 0x81, 0x3c, 0x64, 0x1e, 0x0e, 0x8f, 0x15, 0xaa, 0xac, + 0x11, 0x79, 0x09, 0x3b, 0x94, 0x1a, 0x01, 0x2f, 0x04, 0x82, 0x10, 0x0f, 0x24, 0x50, 0x8c, 0x07, + 0x2f, 0xcd, 0xb4, 0x1d, 0xca, 0xc6, 0x76, 0xa7, 0x74, 0xa7, 0x0d, 0x84, 0x70, 0xf1, 0xe2, 0xd5, + 0xe8, 0x5f, 0x62, 0xe2, 0x41, 0x4d, 0xf4, 0x68, 0x38, 0x92, 0x78, 0xf1, 0x64, 0x0c, 0xf8, 0x87, + 0x98, 0x9d, 0xfd, 0x2d, 0xdd, 0xb1, 0xbb, 0xdd, 0x45, 0x38, 0xb5, 0x3b, 0xfd, 0xbd, 0x7c, 0x7e, + 0x2f, 0xfb, 0xed, 0xe0, 0xbb, 0x55, 0xd6, 0x62, 0x36, 0x97, 0xd4, 0xfd, 0xa4, 0xbc, 0x2e, 0x4a, + 0x7b, 0x8e, 0x14, 0x0d, 0x56, 0xe1, 0x74, 0xbf, 0xc9, 0x1b, 0x87, 0x66, 0xbd, 0x21, 0xa4, 0x20, + 0x37, 0xc0, 0xcc, 0x74, 0x3f, 0xcd, 0xa0, 0x59, 0x7a, 0xb4, 0x22, 0x44, 0xa5, 0xca, 0x29, 0xab, + 0x5b, 0x94, 0xd9, 0xb6, 0x90, 0x4c, 0x5a, 0xc2, 0x76, 0x3c, 0xc7, 0xf4, 0x74, 0x49, 0x38, 0x35, + 0xe1, 0xd0, 0x22, 0x73, 0x20, 0x22, 0x6d, 0x65, 0x8b, 0x5c, 0xb2, 0x2c, 0xad, 0xb3, 0x8a, 0x65, + 0x2b, 0x63, 0xb0, 0x9d, 0x88, 0x66, 0xa9, 0xb3, 0x06, 0xab, 0xf9, 0x31, 0x67, 0xa3, 0xed, 0x1c, + 0xc9, 0x9e, 0xf3, 0x02, 0x3c, 0xc5, 0x9b, 0xab, 0x87, 0x42, 0x99, 0x4b, 0x66, 0x55, 0xfd, 0xe8, + 0x66, 0xb4, 0xf9, 0xae, 0x75, 0xc0, 0x24, 0x2f, 0x17, 0x34, 0x9a, 0x6c, 0x17, 0xea, 0x86, 0x68, + 0x59, 0x65, 0xde, 0x28, 0xd4, 0xb8, 0x64, 0x65, 0x26, 0x19, 0xb8, 0x64, 0x82, 0x4d, 0xf1, 0xdb, + 0x51, 0x12, 0x96, 0xdf, 0x88, 0x91, 0x8a, 0xa8, 0x08, 0xf5, 0x95, 0xba, 0xdf, 0xbc, 0x53, 0x63, + 0x04, 0x93, 0x6d, 0xb7, 0x81, 0x5b, 0x2a, 0x7b, 0x9e, 0xef, 0x37, 0xb9, 0x23, 0x8d, 0xa7, 0xf8, + 0x3f, 0xed, 0xd4, 0xa9, 0x0b, 0xdb, 0xe1, 0x64, 0x19, 0xf7, 0x7b, 0x94, 0xff, 0xa3, 0xdb, 0x68, + 0xf2, 0x9f, 0xf9, 0x71, 0x33, 0x72, 0x82, 0xa6, 0xe7, 0xba, 0xfa, 0xd7, 0xc9, 0xf7, 0x5b, 0xa9, + 0x3c, 0xb8, 0x19, 0x39, 0x7c, 0x53, 0xc5, 0xdd, 0xe0, 0x72, 0xc7, 0x6d, 0xea, 0x8e, 0x67, 0x0c, + 0x69, 0xc9, 0x08, 0xee, 0xb3, 0xec, 0x32, 0x3f, 0x50, 0xe1, 0xff, 0xce, 0x7b, 0x0f, 0xc6, 0x3e, + 0x1e, 0x0d, 0x77, 0x02, 0xaa, 0x6d, 0x3c, 0xe8, 0x04, 0xce, 0x81, 0xed, 0x5e, 0x17, 0xb6, 0x60, + 0x18, 0x20, 0xd4, 0x42, 0x18, 0x1c, 0x38, 0x57, 0xaa, 0xd5, 0x30, 0xce, 0x75, 0x8c, 0xdb, 0x7b, + 0x06, 0xf9, 0x26, 0x4c, 0xaf, 0xff, 0xa6, 0xdb, 0x7f, 0xd3, 0x5b, 0x73, 0x98, 0x82, 0xb9, 0xd5, + 0xf6, 0xcd, 0x07, 0x3c, 0x8d, 0x8f, 0x08, 0x4a, 0xeb, 0xc8, 0x13, 0x59, 0x5a, 0xef, 0x15, 0x4b, + 0x23, 0x1b, 0x1a, 0x7b, 0x0f, 0xf4, 0x2a, 0x8e, 0xdd, 0xe3, 0xd1, 0xe0, 0xc7, 0xda, 0xb3, 0x7c, + 0xe4, 0x12, 0xac, 0x79, 0x0b, 0xef, 0xaf, 0x50, 0x60, 0x6a, 0xfa, 0xcf, 0xed, 0xd2, 0x82, 0xe7, + 0x09, 0xa6, 0x16, 0x34, 0xf7, 0x4b, 0x0b, 0x9e, 0x19, 0xf7, 0xdb, 0x29, 0xd7, 0xbd, 0x97, 0x4a, + 0xdb, 0xea, 0x88, 0xf5, 0x6a, 0xe2, 0xb1, 0x08, 0x2f, 0x20, 0x7d, 0x82, 0x87, 0x76, 0x83, 0x3f, + 0x00, 0xea, 0x64, 0x17, 0x54, 0x2d, 0x10, 0xb0, 0xea, 0x41, 0x8c, 0xdd, 0xf6, 0xe8, 0x43, 0x61, + 0xaf, 0x6b, 0xc7, 0x3e, 0x23, 0xa8, 0xaf, 0x33, 0x51, 0x74, 0x7d, 0xbd, 0x57, 0xae, 0xef, 0xfa, + 0xf6, 0xec, 0x21, 0x34, 0x6a, 0x0b, 0x74, 0x6f, 0x93, 0x4b, 0xb6, 0xc6, 0x24, 0xf3, 0x1b, 0x95, + 0xc6, 0x03, 0xbe, 0x24, 0xc2, 0x60, 0x2f, 0x9e, 0x0d, 0x1b, 0x6a, 0xef, 0xf4, 0x85, 0xda, 0x37, + 0xf1, 0x80, 0x7f, 0x06, 0x65, 0xcf, 0x74, 0xd3, 0xb4, 0x40, 0x18, 0x57, 0x79, 0xa1, 0xf2, 0x8b, + 0x10, 0xf3, 0x2f, 0x31, 0xee, 0x53, 0x09, 0xc9, 0x6b, 0x84, 0xfb, 0xa1, 0x13, 0xb3, 0x5d, 0x22, + 0x76, 0x6a, 0x6f, 0xda, 0x4c, 0x6a, 0xee, 0x95, 0x60, 0x4c, 0xbd, 0xf8, 0xfa, 0xf3, 0x4d, 0xcf, + 0x1d, 0x32, 0x4e, 0xe3, 0xfe, 0xe9, 0xc8, 0x07, 0x84, 0x07, 0x83, 0x02, 0x41, 0x1e, 0xc4, 0xe5, + 0x0a, 0x17, 0xea, 0xf4, 0xc2, 0xa5, 0xfd, 0x00, 0x76, 0x51, 0xc1, 0xce, 0x93, 0x39, 0x9a, 0xf0, + 0xef, 0x96, 0x1e, 0xa9, 0xb7, 0xf4, 0x98, 0xbc, 0x43, 0xf8, 0xdf, 0x60, 0xc8, 0x95, 0x6a, 0x35, + 0x1e, 0x3f, 0x5c, 0xbf, 0xe3, 0xf1, 0x23, 0xf4, 0xd8, 0x98, 0x53, 0xf8, 0xd3, 0x64, 0x32, 0x29, + 0x3e, 0x79, 0x8b, 0x74, 0x9d, 0x4b, 0xd4, 0xf2, 0x10, 0x3d, 0x4d, 0xd4, 0xf2, 0x30, 0xa1, 0x4d, + 0xc4, 0xac, 0x5d, 0x59, 0xc8, 0x27, 0x84, 0x87, 0xb4, 0x37, 0x9c, 0x24, 0x49, 0x1e, 0xa6, 0x62, + 0xe9, 0xc5, 0xcb, 0x3b, 0x02, 0xf6, 0x92, 0xc2, 0xce, 0x91, 0x2c, 0x4d, 0x7a, 0x75, 0xba, 0x58, + 0x95, 0xf7, 0x08, 0x0f, 0x6b, 0x41, 0xdd, 0x5d, 0x49, 0x32, 0xf3, 0x3f, 0x2b, 0x21, 0x4a, 0x58, + 0x8d, 0xac, 0x2a, 0x61, 0x86, 0x4c, 0x25, 0x2e, 0x81, 0x7c, 0x41, 0x78, 0xf8, 0x77, 0xb1, 0x8a, + 0x47, 0x8f, 0x90, 0xc6, 0x78, 0xf4, 0x28, 0x5d, 0x34, 0x96, 0x15, 0xfa, 0x12, 0x59, 0xa0, 0x97, + 0xb8, 0x88, 0xd2, 0x23, 0xff, 0xe8, 0x78, 0xf5, 0xf1, 0xc9, 0x59, 0x06, 0x9d, 0x9e, 0x65, 0xd0, + 0x8f, 0xb3, 0x0c, 0x7a, 0x75, 0x9e, 0x49, 0x9d, 0x9e, 0x67, 0x52, 0xdf, 0xce, 0x33, 0xa9, 0x67, + 0xb4, 0x62, 0xc9, 0xbd, 0x66, 0xd1, 0x2c, 0x89, 0x9a, 0x1e, 0xbc, 0x95, 0xa3, 0x07, 0x7a, 0x06, + 0x79, 0x58, 0xe7, 0x4e, 0xb1, 0x5f, 0xdd, 0x54, 0x73, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xdb, + 0xdb, 0xbf, 0x35, 0x56, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -653,6 +750,8 @@ type QueryClient interface { FixatedParams(ctx context.Context, in *QueryGetFixatedParamsRequest, opts ...grpc.CallOption) (*QueryGetFixatedParamsResponse, error) // Queries a list of FixatedParams items. FixatedParamsAll(ctx context.Context, in *QueryAllFixatedParamsRequest, opts ...grpc.CallOption) (*QueryAllFixatedParamsResponse, error) + // Queries provider metadata. + ProviderMetaData(ctx context.Context, in *QueryProviderMetaDataRequest, opts ...grpc.CallOption) (*QueryProviderMetaDataResponse, error) } type queryClient struct { @@ -717,6 +816,15 @@ func (c *queryClient) FixatedParamsAll(ctx context.Context, in *QueryAllFixatedP return out, nil } +func (c *queryClient) ProviderMetaData(ctx context.Context, in *QueryProviderMetaDataRequest, opts ...grpc.CallOption) (*QueryProviderMetaDataResponse, error) { + out := new(QueryProviderMetaDataResponse) + err := c.cc.Invoke(ctx, "/lavanet.lava.epochstorage.Query/ProviderMetaData", 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. @@ -731,6 +839,8 @@ type QueryServer interface { FixatedParams(context.Context, *QueryGetFixatedParamsRequest) (*QueryGetFixatedParamsResponse, error) // Queries a list of FixatedParams items. FixatedParamsAll(context.Context, *QueryAllFixatedParamsRequest) (*QueryAllFixatedParamsResponse, error) + // Queries provider metadata. + ProviderMetaData(context.Context, *QueryProviderMetaDataRequest) (*QueryProviderMetaDataResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -755,6 +865,9 @@ func (*UnimplementedQueryServer) FixatedParams(ctx context.Context, req *QueryGe func (*UnimplementedQueryServer) FixatedParamsAll(ctx context.Context, req *QueryAllFixatedParamsRequest) (*QueryAllFixatedParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method FixatedParamsAll not implemented") } +func (*UnimplementedQueryServer) ProviderMetaData(ctx context.Context, req *QueryProviderMetaDataRequest) (*QueryProviderMetaDataResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ProviderMetaData not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -868,6 +981,24 @@ func _Query_FixatedParamsAll_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _Query_ProviderMetaData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryProviderMetaDataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ProviderMetaData(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lavanet.lava.epochstorage.Query/ProviderMetaData", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ProviderMetaData(ctx, req.(*QueryProviderMetaDataRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "lavanet.lava.epochstorage.Query", HandlerType: (*QueryServer)(nil), @@ -896,6 +1027,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "FixatedParamsAll", Handler: _Query_FixatedParamsAll_Handler, }, + { + MethodName: "ProviderMetaData", + Handler: _Query_ProviderMetaData_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "lavanet/lava/epochstorage/query.proto", @@ -1307,6 +1442,73 @@ func (m *QueryAllFixatedParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, return len(dAtA) - i, nil } +func (m *QueryProviderMetaDataRequest) 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 *QueryProviderMetaDataRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProviderMetaDataRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + 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 *QueryProviderMetaDataResponse) 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 *QueryProviderMetaDataResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProviderMetaDataResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.MetaData) > 0 { + for iNdEx := len(m.MetaData) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.MetaData[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 @@ -1470,6 +1672,34 @@ func (m *QueryAllFixatedParamsResponse) Size() (n int) { return n } +func (m *QueryProviderMetaDataRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Provider) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryProviderMetaDataResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.MetaData) > 0 { + for _, e := range m.MetaData { + 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 } @@ -2484,6 +2714,172 @@ func (m *QueryAllFixatedParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryProviderMetaDataRequest) 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: QueryProviderMetaDataRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProviderMetaDataRequest: 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 + 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 *QueryProviderMetaDataResponse) 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: QueryProviderMetaDataResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProviderMetaDataResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MetaData", 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.MetaData = append(m.MetaData, ProviderMetadata{}) + if err := m.MetaData[len(m.MetaData)-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/epochstorage/types/query.pb.gw.go b/x/epochstorage/types/query.pb.gw.go index 66cc24ed93..29270cc94c 100644 --- a/x/epochstorage/types/query.pb.gw.go +++ b/x/epochstorage/types/query.pb.gw.go @@ -249,6 +249,60 @@ func local_request_Query_FixatedParamsAll_0(ctx context.Context, marshaler runti } +func request_Query_ProviderMetaData_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProviderMetaDataRequest + 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) + } + + msg, err := client.ProviderMetaData(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ProviderMetaData_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProviderMetaDataRequest + 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) + } + + msg, err := server.ProviderMetaData(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. @@ -393,6 +447,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_ProviderMetaData_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_ProviderMetaData_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_ProviderMetaData_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -554,6 +631,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_ProviderMetaData_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_ProviderMetaData_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_ProviderMetaData_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -569,6 +666,8 @@ var ( pattern_Query_FixatedParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"lavanet", "lava", "epochstorage", "fixated_params", "index"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_FixatedParamsAll_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"lavanet", "lava", "epochstorage", "fixated_params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ProviderMetaData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"lavanet", "lava", "epochstorage", "provider_metadata", "provider"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -583,4 +682,6 @@ var ( forward_Query_FixatedParams_0 = runtime.ForwardResponseMessage forward_Query_FixatedParamsAll_0 = runtime.ForwardResponseMessage + + forward_Query_ProviderMetaData_0 = runtime.ForwardResponseMessage ) diff --git a/x/epochstorage/types/stake_entries.go b/x/epochstorage/types/stake_entries.go index a1a8c5cd81..3fb2470bc7 100644 --- a/x/epochstorage/types/stake_entries.go +++ b/x/epochstorage/types/stake_entries.go @@ -17,6 +17,7 @@ var ( EpochChainIdProviderIndexesPrefix = collections.NewPrefix([]byte("EpochChainIdProviderIndexes/")) ChainIdVaultIndexesPrefix = collections.NewPrefix([]byte("ChainIdVaultIndexes/")) EpochHashesPrefix = collections.NewPrefix([]byte("EpochHash/")) + ProviderMetaDataPrefix = collections.NewPrefix([]byte("ProviderMetaData/")) ) // EpochChainIdProviderIndexes defines a secondary unique index for the keeper's stakeEntries indexed map diff --git a/x/epochstorage/types/stake_entry.pb.go b/x/epochstorage/types/stake_entry.pb.go index a5753318b7..74eeb33f9b 100644 --- a/x/epochstorage/types/stake_entry.pb.go +++ b/x/epochstorage/types/stake_entry.pb.go @@ -36,7 +36,6 @@ type StakeEntry struct { Moniker string `protobuf:"bytes,8,opt,name=moniker,proto3" json:"moniker,omitempty"` DelegateTotal types.Coin `protobuf:"bytes,9,opt,name=delegate_total,json=delegateTotal,proto3" json:"delegate_total"` DelegateCommission uint64 `protobuf:"varint,11,opt,name=delegate_commission,json=delegateCommission,proto3" json:"delegate_commission,omitempty"` - LastChange uint64 `protobuf:"varint,12,opt,name=last_change,json=lastChange,proto3" json:"last_change,omitempty"` BlockReport *BlockReport `protobuf:"bytes,13,opt,name=block_report,json=blockReport,proto3" json:"block_report,omitempty"` Vault string `protobuf:"bytes,14,opt,name=vault,proto3" json:"vault,omitempty"` Description types1.Description `protobuf:"bytes,15,opt,name=description,proto3" json:"description"` @@ -140,13 +139,6 @@ func (m *StakeEntry) GetDelegateCommission() uint64 { return 0 } -func (m *StakeEntry) GetLastChange() uint64 { - if m != nil { - return m.LastChange - } - return 0 -} - func (m *StakeEntry) GetBlockReport() *BlockReport { if m != nil { return m.BlockReport @@ -247,45 +239,44 @@ func init() { } var fileDescriptor_df6302d6b53c056e = []byte{ - // 597 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xbd, 0x4e, 0xdd, 0x30, - 0x18, 0xbd, 0x81, 0x5c, 0xb8, 0xd7, 0x01, 0x0a, 0x86, 0xc1, 0x30, 0x84, 0x14, 0xaa, 0x2a, 0x6a, - 0xa5, 0x58, 0x80, 0xfa, 0x00, 0xbd, 0x14, 0x2a, 0x98, 0xaa, 0x94, 0xa9, 0x4b, 0xe4, 0x24, 0x56, - 0xae, 0x4b, 0x62, 0x47, 0xb1, 0xb9, 0x2a, 0x6f, 0xd1, 0xc7, 0xe8, 0xd8, 0xbd, 0x2f, 0xc0, 0xc8, - 0xd8, 0xa9, 0xaa, 0x60, 0xe8, 0x6b, 0x54, 0xb6, 0x93, 0xfb, 0x33, 0x50, 0x75, 0x49, 0x7c, 0xbe, - 0x73, 0xfc, 0xf9, 0x3b, 0x47, 0x36, 0x78, 0x5d, 0x92, 0x09, 0xe1, 0x54, 0x61, 0xfd, 0xc7, 0xb4, - 0x16, 0xd9, 0x58, 0x2a, 0xd1, 0x90, 0x82, 0x62, 0xa9, 0xc8, 0x35, 0x4d, 0x28, 0x57, 0xcd, 0x6d, - 0x54, 0x37, 0x42, 0x09, 0xb8, 0xdb, 0x8a, 0x23, 0xfd, 0x8f, 0xe6, 0xc5, 0x7b, 0xe1, 0xd3, 0x7d, - 0x28, 0xcf, 0x6b, 0xc1, 0xb8, 0xb2, 0x4d, 0xf6, 0x76, 0x0a, 0x51, 0x08, 0xb3, 0xc4, 0x7a, 0xd5, - 0x56, 0xfd, 0x4c, 0xc8, 0x4a, 0x48, 0x9c, 0x12, 0x49, 0xf1, 0xe4, 0x28, 0xa5, 0x8a, 0x1c, 0xe1, - 0x4c, 0x30, 0xde, 0xf2, 0x2f, 0x5a, 0x5e, 0x0f, 0xc5, 0x78, 0x31, 0x95, 0xb4, 0xb8, 0x55, 0x6d, - 0x91, 0x8a, 0x71, 0x81, 0xcd, 0xd7, 0x96, 0x0e, 0x7e, 0xf4, 0x01, 0xf8, 0xa8, 0x9d, 0x9c, 0x69, - 0x23, 0xf0, 0x0d, 0xe8, 0x1b, 0x5f, 0xc8, 0x09, 0x9c, 0xd0, 0x3b, 0xde, 0x8d, 0x6c, 0xdf, 0x48, - 0x9f, 0x1b, 0xb5, 0x4d, 0xa3, 0x53, 0xc1, 0xf8, 0xc8, 0xbd, 0xfb, 0xb5, 0xdf, 0x8b, 0xad, 0x1a, - 0x22, 0xb0, 0x4a, 0xf2, 0xbc, 0xa1, 0x52, 0xa2, 0xa5, 0xc0, 0x09, 0x87, 0x71, 0x07, 0x61, 0x04, - 0xb6, 0x6d, 0x50, 0xa4, 0xae, 0x4b, 0x46, 0xf3, 0x24, 0x2d, 0x45, 0x76, 0x8d, 0x96, 0x03, 0x27, - 0x74, 0xe3, 0x2d, 0x43, 0xbd, 0xb5, 0xcc, 0x48, 0x13, 0xf0, 0x3d, 0x18, 0x76, 0x81, 0x48, 0xe4, - 0x06, 0xcb, 0xa1, 0x77, 0x7c, 0x18, 0x3d, 0x99, 0x6b, 0x74, 0xd6, 0x6a, 0xdb, 0x71, 0x66, 0x7b, - 0x61, 0x00, 0xbc, 0x82, 0x8a, 0x52, 0x64, 0x44, 0x31, 0xc1, 0x51, 0x3f, 0x70, 0xc2, 0x7e, 0x3c, - 0x5f, 0x82, 0x3b, 0xa0, 0x9f, 0x8d, 0x09, 0xe3, 0x68, 0xc5, 0x8c, 0x6c, 0x81, 0xb6, 0x52, 0x09, - 0xce, 0xae, 0x69, 0x83, 0x06, 0xd6, 0x4a, 0x0b, 0xe1, 0x39, 0xd8, 0xc8, 0x69, 0x49, 0x0b, 0xa2, - 0x68, 0xa2, 0x84, 0x22, 0x25, 0x1a, 0xfe, 0x5f, 0x48, 0xeb, 0xdd, 0xb6, 0x2b, 0xbd, 0x0b, 0x62, - 0xb0, 0x3d, 0xed, 0x93, 0x89, 0xaa, 0x62, 0x52, 0xea, 0x09, 0x3d, 0x13, 0x09, 0xec, 0xa8, 0xd3, - 0x29, 0x03, 0xf7, 0x81, 0x57, 0x12, 0xa9, 0x92, 0x6c, 0x4c, 0x78, 0x41, 0xd1, 0x9a, 0x11, 0x02, - 0x5d, 0x3a, 0x35, 0x15, 0x78, 0x01, 0xd6, 0x4c, 0xac, 0x49, 0x43, 0x6b, 0xd1, 0x28, 0xb4, 0x6e, - 0xe6, 0x7a, 0xf9, 0x8f, 0xdc, 0x4c, 0xd8, 0xb1, 0x51, 0xc7, 0x5e, 0x3a, 0x03, 0x3a, 0x94, 0x09, - 0xb9, 0x29, 0x15, 0xda, 0xb0, 0xa1, 0x18, 0x00, 0x3f, 0x00, 0x2f, 0xa7, 0x32, 0x6b, 0x58, 0x6d, - 0xc2, 0x7c, 0x66, 0xfa, 0x1f, 0x76, 0xbe, 0xbb, 0x4b, 0xd6, 0x59, 0x7f, 0x37, 0x93, 0x8e, 0x86, - 0x3a, 0x81, 0x6f, 0x7f, 0xbe, 0xbf, 0x72, 0xe2, 0xf9, 0x16, 0xfa, 0x9c, 0xcf, 0x84, 0x95, 0x12, - 0x6d, 0x1a, 0x37, 0x16, 0xc0, 0x03, 0xb0, 0xae, 0x17, 0x09, 0xe5, 0x79, 0xa2, 0x58, 0x45, 0xd1, - 0x56, 0xe0, 0x84, 0xcb, 0xb1, 0xa7, 0x8b, 0x67, 0x3c, 0xbf, 0x62, 0x15, 0xbd, 0x74, 0x07, 0xab, - 0x9b, 0x83, 0x4b, 0x77, 0x00, 0x36, 0xbd, 0x83, 0x73, 0xe0, 0x8d, 0x16, 0x87, 0x37, 0x2e, 0xcd, - 0xed, 0x75, 0x63, 0x0b, 0xe0, 0x73, 0xb0, 0x56, 0x12, 0x45, 0xa5, 0x6a, 0xef, 0xde, 0x92, 0x21, - 0x3d, 0x5b, 0x33, 0xdb, 0x47, 0x17, 0x77, 0x0f, 0xbe, 0x73, 0xff, 0xe0, 0x3b, 0xbf, 0x1f, 0x7c, - 0xe7, 0xeb, 0xa3, 0xdf, 0xbb, 0x7f, 0xf4, 0x7b, 0x3f, 0x1f, 0xfd, 0xde, 0x27, 0x5c, 0x30, 0x35, - 0xbe, 0x49, 0xa3, 0x4c, 0x54, 0x78, 0xe1, 0x0d, 0x4f, 0x4e, 0xf0, 0x97, 0xc5, 0x87, 0xac, 0x6e, - 0x6b, 0x2a, 0xd3, 0x15, 0xf3, 0xae, 0x4e, 0xfe, 0x06, 0x00, 0x00, 0xff, 0xff, 0x28, 0x02, 0x10, - 0xfc, 0x3a, 0x04, 0x00, 0x00, + // 585 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xcd, 0x4e, 0xdc, 0x3c, + 0x14, 0x9d, 0x40, 0x06, 0x66, 0x1c, 0x86, 0x6f, 0x30, 0x2c, 0x0c, 0x8b, 0x7c, 0x29, 0x54, 0x55, + 0xd4, 0x4a, 0xb1, 0x00, 0xf5, 0x01, 0x3a, 0x14, 0x2a, 0x58, 0x55, 0x29, 0xab, 0x6e, 0x22, 0x27, + 0xb1, 0x82, 0x4b, 0x62, 0x47, 0xb1, 0x19, 0x95, 0xb7, 0xe8, 0x23, 0x74, 0xd9, 0x65, 0x1f, 0x83, + 0x25, 0xcb, 0xae, 0xaa, 0x0a, 0x16, 0x7d, 0x8d, 0xca, 0x76, 0x32, 0x3f, 0x0b, 0xaa, 0x6e, 0x12, + 0x9f, 0x7b, 0x8e, 0xaf, 0xef, 0x39, 0xb2, 0xc1, 0xab, 0x92, 0x4c, 0x09, 0xa7, 0x0a, 0xeb, 0x3f, + 0xa6, 0xb5, 0xc8, 0xae, 0xa4, 0x12, 0x0d, 0x29, 0x28, 0x96, 0x8a, 0x5c, 0xd3, 0x84, 0x72, 0xd5, + 0xdc, 0x46, 0x75, 0x23, 0x94, 0x80, 0xbb, 0xad, 0x38, 0xd2, 0xff, 0x68, 0x51, 0xbc, 0x17, 0x3e, + 0xdd, 0x87, 0xf2, 0xbc, 0x16, 0x8c, 0x2b, 0xdb, 0x64, 0x6f, 0xa7, 0x10, 0x85, 0x30, 0x4b, 0xac, + 0x57, 0x6d, 0xd5, 0xcf, 0x84, 0xac, 0x84, 0xc4, 0x29, 0x91, 0x14, 0x4f, 0x0f, 0x53, 0xaa, 0xc8, + 0x21, 0xce, 0x04, 0xe3, 0x2d, 0xff, 0xbc, 0xe5, 0xf5, 0x50, 0x8c, 0x17, 0x33, 0x49, 0x8b, 0x5b, + 0xd5, 0x16, 0xa9, 0x18, 0x17, 0xd8, 0x7c, 0x6d, 0x69, 0xff, 0x6b, 0x1f, 0x80, 0x0f, 0xda, 0xc9, + 0xa9, 0x36, 0x02, 0x5f, 0x83, 0xbe, 0xf1, 0x85, 0x9c, 0xc0, 0x09, 0xbd, 0xa3, 0xdd, 0xc8, 0xf6, + 0x8d, 0xf4, 0xb9, 0x51, 0xdb, 0x34, 0x3a, 0x11, 0x8c, 0x4f, 0xdc, 0xbb, 0x9f, 0xff, 0xf7, 0x62, + 0xab, 0x86, 0x08, 0xac, 0x93, 0x3c, 0x6f, 0xa8, 0x94, 0x68, 0x25, 0x70, 0xc2, 0x61, 0xdc, 0x41, + 0x18, 0x81, 0x6d, 0x1b, 0x14, 0xa9, 0xeb, 0x92, 0xd1, 0x3c, 0x49, 0x4b, 0x91, 0x5d, 0xa3, 0xd5, + 0xc0, 0x09, 0xdd, 0x78, 0xcb, 0x50, 0x6f, 0x2c, 0x33, 0xd1, 0x04, 0x7c, 0x07, 0x86, 0x5d, 0x20, + 0x12, 0xb9, 0xc1, 0x6a, 0xe8, 0x1d, 0x1d, 0x44, 0x4f, 0xe6, 0x1a, 0x9d, 0xb6, 0xda, 0x76, 0x9c, + 0xf9, 0x5e, 0x18, 0x00, 0xaf, 0xa0, 0xa2, 0x14, 0x19, 0x51, 0x4c, 0x70, 0xd4, 0x0f, 0x9c, 0xb0, + 0x1f, 0x2f, 0x96, 0xe0, 0x0e, 0xe8, 0x67, 0x57, 0x84, 0x71, 0xb4, 0x66, 0x46, 0xb6, 0x40, 0x5b, + 0xa9, 0x04, 0x67, 0xd7, 0xb4, 0x41, 0x03, 0x6b, 0xa5, 0x85, 0xf0, 0x0c, 0x6c, 0xe6, 0xb4, 0xa4, + 0x05, 0x51, 0x34, 0x51, 0x42, 0x91, 0x12, 0x0d, 0xff, 0x2d, 0xa4, 0x51, 0xb7, 0xed, 0x52, 0xef, + 0x82, 0x18, 0x6c, 0xcf, 0xfa, 0x64, 0xa2, 0xaa, 0x98, 0x94, 0x7a, 0x42, 0xcf, 0x44, 0x02, 0x3b, + 0xea, 0x64, 0xc6, 0xc0, 0x73, 0xb0, 0x61, 0x52, 0x4b, 0x1a, 0x5a, 0x8b, 0x46, 0xa1, 0x91, 0x39, + 0xf6, 0xc5, 0x5f, 0x62, 0x31, 0x59, 0xc6, 0x46, 0x1d, 0x7b, 0xe9, 0x1c, 0x68, 0xcf, 0x53, 0x72, + 0x53, 0x2a, 0xb4, 0x69, 0x3d, 0x1b, 0x00, 0xdf, 0x03, 0x2f, 0xa7, 0x32, 0x6b, 0x58, 0x6d, 0xb2, + 0xfa, 0xcf, 0xf4, 0x3f, 0xe8, 0x6c, 0x75, 0x77, 0xa8, 0x73, 0xf6, 0x76, 0x2e, 0x9d, 0x0c, 0xb5, + 0xc1, 0x6f, 0xbf, 0xbf, 0xbf, 0x74, 0xe2, 0xc5, 0x16, 0xfa, 0x9c, 0x4f, 0x84, 0x95, 0x12, 0x8d, + 0x8d, 0x2b, 0x0b, 0xe0, 0x3e, 0x18, 0xe9, 0x45, 0x42, 0x79, 0x9e, 0x28, 0x56, 0x51, 0xb4, 0x15, + 0x38, 0xe1, 0x6a, 0xec, 0xe9, 0xe2, 0x29, 0xcf, 0x2f, 0x59, 0x45, 0x2f, 0xdc, 0xc1, 0xfa, 0x78, + 0x70, 0xe1, 0x0e, 0xc0, 0xd8, 0xbb, 0x70, 0x07, 0x1b, 0xe3, 0xd1, 0xfe, 0x19, 0xf0, 0x26, 0xcb, + 0x16, 0x8c, 0x57, 0x73, 0x45, 0xdd, 0xd8, 0x02, 0xf8, 0x0c, 0x6c, 0x94, 0x44, 0x51, 0xa9, 0xda, + 0x0b, 0xb6, 0x62, 0x48, 0xcf, 0xd6, 0xcc, 0xf6, 0xc9, 0xf9, 0xdd, 0x83, 0xef, 0xdc, 0x3f, 0xf8, + 0xce, 0xaf, 0x07, 0xdf, 0xf9, 0xf2, 0xe8, 0xf7, 0xee, 0x1f, 0xfd, 0xde, 0x8f, 0x47, 0xbf, 0xf7, + 0x11, 0x17, 0x4c, 0x5d, 0xdd, 0xa4, 0x51, 0x26, 0x2a, 0xbc, 0xf4, 0x50, 0xa7, 0xc7, 0xf8, 0xf3, + 0xf2, 0x6b, 0x55, 0xb7, 0x35, 0x95, 0xe9, 0x9a, 0x79, 0x3c, 0xc7, 0x7f, 0x02, 0x00, 0x00, 0xff, + 0xff, 0xa6, 0xec, 0xe6, 0x19, 0x1f, 0x04, 0x00, 0x00, } func (m *StakeEntry) Marshal() (dAtA []byte, err error) { @@ -351,11 +342,6 @@ func (m *StakeEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x6a } - if m.LastChange != 0 { - i = encodeVarintStakeEntry(dAtA, i, uint64(m.LastChange)) - i-- - dAtA[i] = 0x60 - } if m.DelegateCommission != 0 { i = encodeVarintStakeEntry(dAtA, i, uint64(m.DelegateCommission)) i-- @@ -510,9 +496,6 @@ func (m *StakeEntry) Size() (n int) { if m.DelegateCommission != 0 { n += 1 + sovStakeEntry(uint64(m.DelegateCommission)) } - if m.LastChange != 0 { - n += 1 + sovStakeEntry(uint64(m.LastChange)) - } if m.BlockReport != nil { l = m.BlockReport.Size() n += 1 + l + sovStakeEntry(uint64(l)) @@ -835,25 +818,6 @@ func (m *StakeEntry) Unmarshal(dAtA []byte) error { break } } - case 12: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field LastChange", wireType) - } - m.LastChange = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStakeEntry - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.LastChange |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BlockReport", wireType) diff --git a/x/pairing/README.md b/x/pairing/README.md index 593aaad6b4..a89b0eda88 100644 --- a/x/pairing/README.md +++ b/x/pairing/README.md @@ -90,6 +90,16 @@ A consumer sends requests to a provider's endpoint to communicate with them. The Stake entries' storage is managed by the epochstorage module. For more details, see its README. +Providers `DelegateTotal` is the delegators delegations part for this chain and is calculated by as follows: `total_provider_delegations` * `stake` / `total_stake` + +when: + +`stake` is the providers stake on a specific chain + +`total_stake` is the total stake of the provider across all chains + +`total_provider_delegations` is the total delegations for the provider + #### Unstake A provider can unstake and retrieve their coins. When a provider unstakes, they are removed from the pairing list starting from the next epoch. After a specified number of blocks called `UnstakeHoldBlocks` (a parameter of the epochstorage module), the provider is eligible to receive their coins back. @@ -354,6 +364,7 @@ The pairing module supports the following transactions: | `stake-provider` | chain-id (string), amount (Coin), endpoints ([]Endpoint), geolocation (int32), validator (string, optional), --provider-moniker (string) --grant-provider-gas-fees-auth (bool)| stake a provider in a chain with multiple endpoints | | `unfreeze` | chain-ids ([]string) | unfreeze a provider in multiple chains | | `unstake-provider` | chain-ids ([]string), validator (string, optional) | unstake a provider from multiple chains | +| `move-provider-stake` | src-chain dst-chain amount (Coin)| move provider stake amount from one chain to another | Note, the `Coin` type is from Cosmos-SDK (`cosmos.base.v1beta1.Coin`). From the CLI, use `100ulava` to assign a `Coin` argument. The `Endpoint` type defines a provider endpoint. From the CLI, use "my-provider-grpc-addr.com:9090,1" for one endpoint (includes the endpoint's URL+port and the endpoint's geolocation). When it comes to staking-related transactions, the geolocation argument should encompass the geolocations of all the endpoints combined. diff --git a/x/pairing/client/cli/tx.go b/x/pairing/client/cli/tx.go index 4ee4bb6fd2..545f50eb2e 100644 --- a/x/pairing/client/cli/tx.go +++ b/x/pairing/client/cli/tx.go @@ -48,6 +48,8 @@ func GetTxCmd() *cobra.Command { cmd.AddCommand(CmdUnfreeze()) cmd.AddCommand(CmdModifyProvider()) cmd.AddCommand(CmdSimulateRelayPayment()) + cmd.AddCommand(CmdMoveProviderStake()) + cmd.AddCommand(CmdDistributeProviderStake()) // this line is used by starport scaffolding # 1 @@ -134,12 +136,10 @@ func NewSubmitUnstakeProposalTxCmd() *cobra.Command { content.ProvidersInfo = []types.ProviderUnstakeInfo{{Provider: providerEntry.Address, ChainId: providerEntry.Chain}} content.DelegatorsSlashing = []types.DelegatorSlashing{} for _, delegator := range delegators.Delegations { - if delegator.ChainID == providerEntry.Chain { - content.DelegatorsSlashing = append(content.DelegatorsSlashing, types.DelegatorSlashing{ - Delegator: delegator.Delegator, - SlashingAmount: sdk.NewCoin(commontypes.TokenDenom, delegator.Amount.Amount.MulRaw(int64(slashfactor)).QuoRaw(100)), - }) - } + content.DelegatorsSlashing = append(content.DelegatorsSlashing, types.DelegatorSlashing{ + Delegator: delegator.Delegator, + SlashingAmount: sdk.NewCoin(commontypes.TokenDenom, delegator.Amount.Amount.MulRaw(int64(slashfactor)).QuoRaw(100)), + }) } } diff --git a/x/pairing/client/cli/tx_distribute_provider_stake.go b/x/pairing/client/cli/tx_distribute_provider_stake.go new file mode 100644 index 0000000000..344af3d3ec --- /dev/null +++ b/x/pairing/client/cli/tx_distribute_provider_stake.go @@ -0,0 +1,164 @@ +package cli + +import ( + "context" + "fmt" + "sort" + "strconv" + "strings" + + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + commontypes "github.com/lavanet/lava/v3/utils/common/types" + epochstoragetypes "github.com/lavanet/lava/v3/x/epochstorage/types" + "github.com/lavanet/lava/v3/x/pairing/types" + "github.com/spf13/cobra" +) + +var _ = strconv.Itoa(0) + +func CmdDistributeProviderStake() *cobra.Command { + cmd := &cobra.Command{ + Use: "distribute-provider-stake chain,%,chain,%", + Short: "redistribute providers stake between all the chains it is staked on.", + Long: `sends batch of movestake tx to redistribute the total stake according to the users input, the total percentages must be exactly 100`, + Example: `lavad tx pairing distribute-provider-stake chain0,33,chain1,33,chain2,34"`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + provider := clientCtx.GetFromAddress().String() + + pairingQuerier := types.NewQueryClient(clientCtx) + ctx := context.Background() + response, err := pairingQuerier.Provider(ctx, &types.QueryProviderRequest{Address: provider}) + if err != nil { + return err + } + + msgs, err := CalculateDistbiruitions(provider, response.StakeEntries, args[0]) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msgs...) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} + +type data struct { + chain string + original math.Int + percent sdk.Dec + target math.Int + diff math.Int +} + +func CalculateDistbiruitions(provider string, entries []epochstoragetypes.StakeEntry, distributionsArg string) ([]sdk.Msg, error) { + splitedArgs := strings.Split(distributionsArg, ",") + if len(splitedArgs)%2 != 0 { + return nil, fmt.Errorf("args must: chain,percent,chain,percent") + } + + totalStake := sdk.NewCoin(commontypes.TokenDenom, sdk.ZeroInt()) + totalP := sdk.ZeroDec() + distributions := []data{} + for i := 0; i < len(splitedArgs); i += 2 { + p, err := sdk.NewDecFromStr(splitedArgs[i+1]) + if err != nil { + return nil, err + } + for _, e := range entries { + if splitedArgs[i] == e.Chain { + distributions = append(distributions, data{chain: e.Chain, original: e.Stake.Amount, percent: p}) + totalStake = totalStake.Add(e.Stake) + totalP = totalP.Add(p) + } + } + } + + if len(distributions) != len(entries) { + return nil, fmt.Errorf("must specify percentages for all chains the provider is staked on") + } + if !totalP.Equal(sdk.NewDec(100)) { + return nil, fmt.Errorf("total percentages must be 100") + } + + left := totalStake + excesses := []data{} + deficits := []data{} + for i := 0; i < len(distributions); i++ { + if i == len(distributions)-1 { + distributions[i].target = left.Amount + } else { + distributions[i].target = distributions[i].percent.MulInt(totalStake.Amount).QuoInt64(100).RoundInt() + } + left = left.SubAmount(distributions[i].target) + + if distributions[i].original.GT(distributions[i].target) { + distributions[i].diff = distributions[i].original.Sub(distributions[i].target) + excesses = append(excesses, distributions[i]) + } else if distributions[i].original.LT(distributions[i].target) { + distributions[i].diff = distributions[i].target.Sub(distributions[i].original) + deficits = append(deficits, distributions[i]) + } + } + + // Sort excesses and deficits by points, descending order + sort.Slice(excesses, func(i, j int) bool { + return excesses[i].diff.GT(excesses[j].diff) + }) + sort.Slice(deficits, func(i, j int) bool { + return deficits[i].diff.GT(deficits[j].diff) + }) + + // Match excesses and deficits + msgs := []sdk.Msg{} + excessIdx, deficitIdx := 0, 0 + for excessIdx < len(excesses) && deficitIdx < len(deficits) { + // Move the smaller of the excess or deficit + tokensToMove := excesses[excessIdx].diff + if excesses[excessIdx].diff.GT(deficits[deficitIdx].diff) { + tokensToMove = deficits[deficitIdx].diff + } + + msg := types.NewMsgMoveProviderStake(provider, excesses[excessIdx].chain, deficits[deficitIdx].chain, sdk.NewCoin(commontypes.TokenDenom, tokensToMove)) + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + msgs = append(msgs, msg) + excesses[excessIdx].diff = excesses[excessIdx].diff.Sub(tokensToMove) + deficits[deficitIdx].diff = deficits[deficitIdx].diff.Sub(tokensToMove) + + // If an excess or deficit is fully resolved, move to the next one + if excesses[excessIdx].diff.IsZero() { + excessIdx++ + } + if deficits[deficitIdx].diff.IsZero() { + deficitIdx++ + } + } + + for _, item := range deficits { + if !item.diff.IsZero() { + return nil, fmt.Errorf("failed to distribute provider stake") + } + } + + for _, item := range excesses { + if !item.diff.IsZero() { + return nil, fmt.Errorf("failed to distribute provider stake") + } + } + + return msgs, nil +} diff --git a/x/pairing/client/cli/tx_modify_provider.go b/x/pairing/client/cli/tx_modify_provider.go index cf9cb943ad..66e9404011 100644 --- a/x/pairing/client/cli/tx_modify_provider.go +++ b/x/pairing/client/cli/tx_modify_provider.go @@ -122,7 +122,7 @@ func CmdModifyProvider() *cobra.Command { if !providerEntry.Stake.IsEqual(newStake) { if cmd.Flags().Changed(ValidatorFlag) { - validator, err = cmd.Flags().GetString(types.FlagMoniker) + validator, err = cmd.Flags().GetString(ValidatorFlag) if err != nil { return err } diff --git a/x/pairing/client/cli/tx_move_provider_stake.go b/x/pairing/client/cli/tx_move_provider_stake.go new file mode 100644 index 0000000000..98b6386d8f --- /dev/null +++ b/x/pairing/client/cli/tx_move_provider_stake.go @@ -0,0 +1,53 @@ +package cli + +import ( + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v3/x/pairing/types" + "github.com/spf13/cobra" +) + +var _ = strconv.Itoa(0) + +func CmdMoveProviderStake() *cobra.Command { + cmd := &cobra.Command{ + Use: "move-provider-stake src-chain dst-chain amount", + Short: "moves providers stak from one chain to another", + Long: `moves a provider stake amount from source chain to destination chain that the provider is taked in`, + Example: `required flags: --from alice. + lavad tx pairing move-provider-stake chain0 chain1 100ulava"`, + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + srcChain := args[0] + dstChain := args[1] + amount, err := sdk.ParseCoinNormalized(args[2]) + if err != nil { + return fmt.Errorf("failed to parse amount: %w", err) + } + + msg := types.NewMsgMoveProviderStake( + clientCtx.GetFromAddress().String(), + srcChain, + dstChain, + amount, + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/pairing/handler.go b/x/pairing/handler.go index 9f036f9c2b..a472ebb735 100644 --- a/x/pairing/handler.go +++ b/x/pairing/handler.go @@ -33,6 +33,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler { case *types.MsgUnfreezeProvider: res, err := msgServer.UnfreezeProvider(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgMoveProviderStake: + res, err := msgServer.MoveProviderStake(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) // this line is used by starport scaffolding # 1 default: errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg) diff --git a/x/pairing/keeper/cu_tracker_test.go b/x/pairing/keeper/cu_tracker_test.go index eef6daccc7..d8f96c501d 100644 --- a/x/pairing/keeper/cu_tracker_test.go +++ b/x/pairing/keeper/cu_tracker_test.go @@ -144,17 +144,17 @@ func TestTrackedCuWithDelegations(t *testing.T) { clientAcct, _ := ts.GetAccount(common.CONSUMER, 0) providerAcct, provider := ts.GetAccount(common.PROVIDER, 0) - // change the provider's delegation limit and commission - stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) - require.True(t, found) - stakeEntry.DelegateCommission = 0 - ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) + // change the provider's and commission + metadata, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider) + require.NoError(t, err) + metadata.DelegateCommission = 0 + ts.Keepers.Epochstorage.SetMetadata(ts.Ctx, metadata) ts.AdvanceEpoch() // delegate testStake/2 (with commission=0) -> provider should get 66% of the reward _, delegator := ts.AddAccount(common.CONSUMER, 1, testBalance) - _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake/2))) + _, err = ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake/2))) require.NoError(t, err) ts.AdvanceEpoch() @@ -484,16 +484,16 @@ func TestProviderMonthlyPayoutQuery(t *testing.T) { require.NoError(t, err) ts.AdvanceEpoch() - // change the provider's delegation limit and commission - stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) - require.True(t, found) - stakeEntry.DelegateCommission = 0 - ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) + // change the provider's and commission + metadata, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider) + require.NoError(t, err) + metadata.DelegateCommission = 10 + ts.Keepers.Epochstorage.SetMetadata(ts.Ctx, metadata) ts.AdvanceEpoch() // delegate testStake/2 (with commission=0) -> provider should get 66% of the reward _, delegator := ts.AddAccount(common.CONSUMER, 1, testBalance) - _, err = ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake/2))) + _, err = ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake/2))) require.NoError(t, err) ts.AdvanceEpoch() ts.AdvanceMonths(1).AdvanceEpoch() // advance first month of delegation so it'll apply @@ -515,12 +515,12 @@ func TestProviderMonthlyPayoutQuery(t *testing.T) { } ts.relayPaymentWithoutPay(relayPaymentMessage, true) - // check for expected balance: credit*100/200 (from spec1) + credit*(100/200)*(2/3) (from spec, considering delegations) - // for credit=100 (first month there was no use, so no credit was spent), expected monthly payout is 50+33 - expectedTotalPayout := uint64(83) + // check for expected balance: (credit*100/200 (from spec1) + credit*(100/200))*(4/5+1/5*commission(10%) (from spec, considering delegations) + // for credit=100 (first month there was no use, so no credit was spent), expected monthly payout is 90 + expectedTotalPayout := uint64(82) expectedPayouts := []types.SubscriptionPayout{ - {Subscription: clientAcc.Addr.String(), ChainId: ts.spec.Index, Amount: 33}, - {Subscription: clientAcc.Addr.String(), ChainId: spec1.Index, Amount: 50}, + {Subscription: clientAcc.Addr.String(), ChainId: ts.spec.Index, Amount: 41}, + {Subscription: clientAcc.Addr.String(), ChainId: spec1.Index, Amount: 41}, } res, err := ts.QueryPairingProviderMonthlyPayout(provider) require.NoError(t, err) @@ -603,16 +603,16 @@ func TestProviderMonthlyPayoutQueryWithContributor(t *testing.T) { require.NoError(t, err) ts.AdvanceEpoch() - // change the provider's delegation limit and commission - stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) - require.True(t, found) - stakeEntry.DelegateCommission = 0 - ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) + // change the provider's and commission + metadata, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider) + require.NoError(t, err) + metadata.DelegateCommission = 10 + ts.Keepers.Epochstorage.SetMetadata(ts.Ctx, metadata) ts.AdvanceEpoch() // delegate testStake/2 (with commission=0) -> provider should get 66% of the reward _, delegator := ts.AddAccount(common.CONSUMER, 1, testBalance) - _, err = ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake/2))) + _, err = ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake/2))) require.NoError(t, err) delegationTime := ts.BlockTime() ts.AdvanceEpoch() @@ -622,7 +622,7 @@ func TestProviderMonthlyPayoutQueryWithContributor(t *testing.T) { // now since we want to check the expected reward, before it gets transferred). So, we need to artificially // change the delegations' timestamp to be a month forward fakeTimestamp := ts.BlockTime().AddDate(0, -2, 0) - err = ts.ChangeDelegationTimestamp(provider, delegator, ts.spec.Index, ts.BlockHeight(), ts.GetNextMonth(fakeTimestamp)) + err = ts.ChangeDelegationTimestamp(provider, delegator, ts.BlockHeight(), ts.GetNextMonth(fakeTimestamp)) require.NoError(t, err) // send two relay payments in spec and spec1 @@ -641,13 +641,13 @@ func TestProviderMonthlyPayoutQueryWithContributor(t *testing.T) { } ts.relayPaymentWithoutPay(relayPaymentMessage, true) - // check for expected balance: planPrice*100/200 (from spec1) + planPrice*(100/200)*(2/3) (from spec, considering delegations) + // check for expected balance: planPrice*(100/200 (from spec1) + planPrice*(100/200))*(4/5 + 1/5*0.1) (from spec, considering delegations) // for planPrice=100, expected monthly payout is 50 (spec1 with contributor) + 33 (normal spec no contributor) expectedContributorPay := uint64(12) // half the plan payment for spec1:25 then divided between contributors half half rounded down - expectedTotalPayout := uint64(83) - expectedContributorPay*2 + expectedTotalPayout := uint64(85) - expectedContributorPay*2 expectedPayouts := []types.SubscriptionPayout{ - {Subscription: clientAcc.Addr.String(), ChainId: ts.spec.Index, Amount: 33}, - {Subscription: clientAcc.Addr.String(), ChainId: spec1.Index, Amount: 26}, // 50 - 26 for contributors (each contributor gets 12) + {Subscription: clientAcc.Addr.String(), ChainId: ts.spec.Index, Amount: 41}, + {Subscription: clientAcc.Addr.String(), ChainId: spec1.Index, Amount: 20}, // 50 - 26 for contributors (each contributor gets 12) } res, err := ts.QueryPairingProviderMonthlyPayout(provider) require.NoError(t, err) @@ -687,7 +687,7 @@ func TestProviderMonthlyPayoutQueryWithContributor(t *testing.T) { // advance month + blocksToSave + 1 to trigger the monthly payment // (also restore delegation original timestamp) oldBalance := ts.GetBalance(providerAcct.Vault.Addr) - err = ts.ChangeDelegationTimestamp(provider, delegator, ts.spec.Index, ts.BlockHeight(), ts.GetNextMonth(delegationTime)) + err = ts.ChangeDelegationTimestamp(provider, delegator, ts.BlockHeight(), ts.GetNextMonth(delegationTime)) require.NoError(t, err) ts.AdvanceMonths(1) diff --git a/x/pairing/keeper/delegator_rewards_test.go b/x/pairing/keeper/delegator_rewards_test.go index fd9ba80d3c..17baf1e5bb 100644 --- a/x/pairing/keeper/delegator_rewards_test.go +++ b/x/pairing/keeper/delegator_rewards_test.go @@ -6,7 +6,6 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v3/testutil/common" - "github.com/lavanet/lava/v3/utils/lavaslices" "github.com/lavanet/lava/v3/utils/sigs" dualstakingtypes "github.com/lavanet/lava/v3/x/dualstaking/types" "github.com/lavanet/lava/v3/x/pairing/types" @@ -72,25 +71,26 @@ func TestProviderDelegatorsRewards(t *testing.T) { if amount1.IsZero() { amount1.Amount = sdk.OneInt() } - _, err = ts.TxDualstakingDelegate(delegator1, provider, ts.spec.Index, amount1) + _, err = ts.TxDualstakingDelegate(delegator1, provider, amount1) require.NoError(t, err) amount2 := sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(tt.d2Amount*delegationAmount/100)) - _, err = ts.TxDualstakingDelegate(delegator2, provider, ts.spec.Index, amount2) + _, err = ts.TxDualstakingDelegate(delegator2, provider, amount2) require.NoError(t, err) ts.AdvanceEpoch() // apply delegations // change delegation traits of stake entry and get the modified one - stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) - require.True(t, found) - stakeEntry.DelegateCommission = tt.commission - ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) + metadata, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider) + require.NoError(t, err) + metadata.DelegateCommission = tt.commission + ts.Keepers.Epochstorage.SetMetadata(ts.Ctx, metadata) + ts.AdvanceEpoch() - stakeEntry, found = ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) + stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) require.True(t, found) // check that there are two delegators - res, err := ts.QueryDualstakingProviderDelegators(provider, false) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 3, len(res.Delegations)) @@ -187,33 +187,41 @@ func TestProviderRewardWithCommission(t *testing.T) { ts.AdvanceEpoch() // to apply pairing delegationAmount1 := sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake)) - _, err = ts.TxDualstakingDelegate(delegator1, provider, ts.spec.Index, delegationAmount1) + _, err = ts.TxDualstakingDelegate(delegator1, provider, delegationAmount1) require.NoError(t, err) ts.AdvanceEpoch() // apply delegations - stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) - require.True(t, found) - // ** provider's commission is 100% ** // + metadata, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider) + require.NoError(t, err) + metadata.DelegateCommission = 100 + ts.Keepers.Epochstorage.SetMetadata(ts.Ctx, metadata) - stakeEntry.DelegateCommission = 100 - ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) ts.AdvanceEpoch() - stakeEntry, found = ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) + stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) require.True(t, found) - res, err := ts.QueryDualstakingProviderDelegators(provider, false) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) // the expected reward for the provider with 100% commission is the total rewards (delegators get nothing) currentTimestamp := ts.Ctx.BlockTime().UTC().Unix() - relevantDelegations := lavaslices.Filter(res.Delegations, - func(d dualstakingtypes.Delegation) bool { - return d.ChainID == ts.spec.Index && d.IsFirstWeekPassed(currentTimestamp) - }) totalReward := sdk.NewCoins(sdk.NewCoin(ts.TokenDenom(), math.NewInt(int64(relayCuSum)))) - providerReward, _ := ts.Keepers.Dualstaking.CalcRewards(ts.Ctx, stakeEntry, totalReward, relevantDelegations) + + relevantDelegations := []dualstakingtypes.Delegation{} + totalDelegations := sdk.ZeroInt() + var selfdelegation dualstakingtypes.Delegation + for _, d := range res.Delegations { + if d.Delegator != stakeEntry.Vault { + selfdelegation = d + } else if d.IsFirstWeekPassed(currentTimestamp) { + relevantDelegations = append(relevantDelegations, d) + totalDelegations = totalDelegations.Add(d.Amount.Amount) + } + } + + providerReward, _ := ts.Keepers.Dualstaking.CalcRewards(ts.Ctx, totalReward, totalDelegations, selfdelegation, relevantDelegations, stakeEntry.DelegateCommission) require.True(t, totalReward.IsEqual(providerReward)) @@ -227,8 +235,11 @@ func TestProviderRewardWithCommission(t *testing.T) { require.Equal(t, 0, len(resRewards.Rewards)) // ** provider's commission is 0% ** // - stakeEntry.DelegateCommission = 0 - ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) + metadata, err1 := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider) + require.NoError(t, err1) + metadata.DelegateCommission = 0 + ts.Keepers.Epochstorage.SetMetadata(ts.Ctx, metadata) + ts.AdvanceEpoch() // the expected reward for the provider with 0% commission is half of the total rewards @@ -301,16 +312,16 @@ func TestQueryDelegatorRewards(t *testing.T) { ts.AdvanceEpoch() - makeProviderCommissionZero(ts, ts.spec.Index, provider1) - makeProviderCommissionZero(ts, spec1.Index, provider1) - makeProviderCommissionZero(ts, ts.spec.Index, provider2) + makeProviderCommissionZero(ts, provider1) + makeProviderCommissionZero(ts, provider1) + makeProviderCommissionZero(ts, provider2) delegationAmount := sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake)) - _, err = ts.TxDualstakingDelegate(delegator1, provider1, ts.spec.Index, delegationAmount) + _, err = ts.TxDualstakingDelegate(delegator1, provider1, delegationAmount) require.NoError(t, err) - _, err = ts.TxDualstakingDelegate(delegator1, provider1, spec1.Index, delegationAmount) + _, err = ts.TxDualstakingDelegate(delegator1, provider1, delegationAmount) require.NoError(t, err) - _, err = ts.TxDualstakingDelegate(delegator1, provider2, ts.spec.Index, delegationAmount) + _, err = ts.TxDualstakingDelegate(delegator1, provider2, delegationAmount) require.NoError(t, err) ts.AdvanceEpoch() // apply delegations @@ -337,12 +348,11 @@ func TestQueryDelegatorRewards(t *testing.T) { chainID string expectedRewards int64 }{ - {"assigned provider+chainID", delegator1, provider1, ts.spec.Index, (planPrice * 100) / (200 * 2)}, + {"assigned provider+chainID", delegator1, provider1, ts.spec.Index, 2 * (planPrice * 100) / (200 * 2)}, {"assigned provider", delegator1, provider1, "", 2 * ((planPrice * 100) / (200 * 2))}, {"nothing assigned", delegator1, "", "", (2 * ((planPrice * 100) / (200 * 2))) + (planPrice*100)/(100*2)}, {"invalid delegator", delegator2, provider2, spec1.Index, 0}, {"invalid provider", delegator1, provider3, ts.spec.Index, 0}, - {"invalid chain ID", delegator1, provider2, spec1.Index, 0}, } for _, tt := range tests { @@ -410,11 +420,13 @@ func TestVaultProviderDelegatorRewardsQuery(t *testing.T) { } } -func makeProviderCommissionZero(ts *tester, chainID string, provider string) { - stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, chainID, provider) - require.True(ts.T, found) - stakeEntry.DelegateCommission = 0 - ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) +func makeProviderCommissionZero(ts *tester, provider string) { + metadata, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider) + if err != nil { + panic(err) + } + metadata.DelegateCommission = 0 + ts.Keepers.Epochstorage.SetMetadata(ts.Ctx, metadata) ts.AdvanceEpoch() } @@ -430,12 +442,12 @@ func TestDelegationTimestamp(t *testing.T) { // delegate and check the timestamp is equal to current time + month currentTimeAfterMonth := ts.GetNextMonth(ts.BlockTime()) - _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) + _, err := ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) require.NoError(t, err) ts.AdvanceEpoch() // apply delegations - res, err := ts.QueryDualstakingProviderDelegators(provider, false) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) // expect two because of provider self delegation + delegator for _, d := range res.Delegations { @@ -447,11 +459,11 @@ func TestDelegationTimestamp(t *testing.T) { // advance time and delegate again to verify that the timestamp hasn't changed ts.AdvanceMonths(1) expectedDelegation := sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(2*testStake)) - _, err = ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) + _, err = ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) require.NoError(t, err) ts.AdvanceEpoch() // apply delegations - res, err = ts.QueryDualstakingProviderDelegators(provider, false) + res, err = ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) // expect two because of provider self delegation + delegator for _, d := range res.Delegations { @@ -481,11 +493,11 @@ func TestDelegationFirstMonthPairing(t *testing.T) { // delegate and check the delegation's timestamp is equal than nowPlusMonthTime nowPlusMonthTime := ts.GetNextMonth(ts.BlockTime()) - _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) + _, err := ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) require.NoError(t, err) ts.AdvanceEpoch() // apply delegations - res, err := ts.QueryDualstakingProviderDelegators(provider, false) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) // expect two because of provider self delegation + delegator for _, d := range res.Delegations { @@ -518,16 +530,16 @@ func TestDelegationFirstMonthReward(t *testing.T) { require.True(t, found) ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) ts.AdvanceEpoch() - makeProviderCommissionZero(ts, ts.spec.Index, provider) + makeProviderCommissionZero(ts, provider) // delegate and check the delegation's timestamp is equal to nowPlusMonthTime nowPlusMonthTime := ts.GetNextMonth(ts.BlockTime()) - _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) + _, err := ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) require.NoError(t, err) ts.AdvanceEpoch() // apply delegations - res, err := ts.QueryDualstakingProviderDelegators(provider, false) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) // expect two because of provider self delegation + delegator for _, d := range res.Delegations { @@ -541,7 +553,7 @@ func TestDelegationFirstMonthReward(t *testing.T) { // this, we'll call the reward calculation function directly with a fabricated reward just to // verify that the delegator gets nothing from the total reward fakeReward := sdk.NewCoins(sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) - providerReward, _, err := ts.Keepers.Dualstaking.RewardProvidersAndDelegators(ts.Ctx, provider, ts.spec.Index, + providerReward, err := ts.Keepers.Dualstaking.RewardProvidersAndDelegators(ts.Ctx, provider, ts.spec.Index, fakeReward, subscriptiontypes.ModuleName, true, true, true) require.NoError(t, err) require.True(t, fakeReward.IsEqual(providerReward)) // if the delegator got anything, this would fail @@ -572,17 +584,17 @@ func TestRedelegationFirstMonthReward(t *testing.T) { require.True(t, found) ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, stakeEntry) ts.AdvanceEpoch() - makeProviderCommissionZero(ts, ts.spec.Index, provider1) - makeProviderCommissionZero(ts, ts.spec.Index, provider) + makeProviderCommissionZero(ts, provider1) + makeProviderCommissionZero(ts, provider) // delegate and check the delegation's timestamp is equal to nowPlusMonthTime nowPlusMonthTime := ts.GetNextMonth(ts.BlockTime()) - _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) + _, err := ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) require.NoError(t, err) ts.AdvanceEpoch() // apply delegations - res, err := ts.QueryDualstakingProviderDelegators(provider, false) + res, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) require.Equal(t, 2, len(res.Delegations)) // expect two because of provider self delegation + delegator for _, d := range res.Delegations { @@ -594,7 +606,7 @@ func TestRedelegationFirstMonthReward(t *testing.T) { // advance a month a redelegate some of the funds to the second provider ts.AdvanceMonths(1) redelegateAmount := sdk.NewCoin(ts.TokenDenom(), res.Delegations[0].Amount.Amount.QuoRaw(2)) - _, err = ts.TxDualstakingRedelegate(delegator, provider, provider1, ts.spec.Index, ts.spec.Index, redelegateAmount) + _, err = ts.TxDualstakingRedelegate(delegator, provider, provider1, redelegateAmount) require.NoError(t, err) // to trigger the payment's code, we need to advance a month+blocksToSave. If we do that, @@ -603,11 +615,11 @@ func TestRedelegationFirstMonthReward(t *testing.T) { // verify that the delegator gets nothing from the total reward from provider1 but does get // reward from provider fakeReward := sdk.NewCoins(sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) - provider1Reward, _, err := ts.Keepers.Dualstaking.RewardProvidersAndDelegators(ts.Ctx, provider1, ts.spec.Index, + provider1Reward, err := ts.Keepers.Dualstaking.RewardProvidersAndDelegators(ts.Ctx, provider1, ts.spec.Index, fakeReward, subscriptiontypes.ModuleName, true, false, true) require.NoError(t, err) require.True(t, fakeReward.IsEqual(provider1Reward)) // if the delegator got anything, this would fail - providerReward, _, err := ts.Keepers.Dualstaking.RewardProvidersAndDelegators(ts.Ctx, provider, ts.spec.Index, + providerReward, err := ts.Keepers.Dualstaking.RewardProvidersAndDelegators(ts.Ctx, provider, ts.spec.Index, fakeReward, subscriptiontypes.ModuleName, true, false, true) require.NoError(t, err) require.False(t, fakeReward.IsEqual(providerReward)) // the delegator should have rewards @@ -620,3 +632,84 @@ func TestRedelegationFirstMonthReward(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, len(resRewards.Rewards)) } + +// TestDelegatorRewardProviderAddingChain checks that a delegator gets rewards even if the provider moves stake between chains +func TestDelegatorRewardProviderAddingChain(t *testing.T) { + ts := newTester(t) + ts.setupForPayments(1, 0, 2) // 1 providers, 1 client, 1 providersToPair + ts.AddAccount(common.CONSUMER, 1, testBalance) // add delegator1 + + providerAcc, provider := ts.GetAccount(common.PROVIDER, 0) + _, delegator := ts.GetAccount(common.CONSUMER, 1) + clientAcc, client := ts.AddAccount(common.CONSUMER, 0, testBalance) + makeProviderCommissionZero(ts, provider) + + _, err := ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(testStake))) + require.NoError(t, err) + ts.AdvanceEpoch() // apply delegations + + _, err = ts.TxSubscriptionBuy(client, client, ts.plan.Index, 1, true, false) + require.NoError(t, err) + + ts.AdvanceEpoch() // to apply pairing + + relayPaymentMessage := sendRelay(ts, provider, clientAcc, []string{ts.spec.Index}) + _, err = ts.TxPairingRelayPayment(relayPaymentMessage.Creator, relayPaymentMessage.Relays...) + require.Nil(ts.T, err) + + // advance month + blocksToSave + 1 to trigger the provider monthly payment + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + ts.AdvanceBlocks(ts.BlocksToSave() + 1) + + // delegator should get half of the payment + res, err := ts.QueryDualstakingDelegatorRewards(delegator, provider, "") + require.Nil(ts.T, err) + require.Equal(ts.T, ts.plan.Price.Amount.QuoRaw(2), res.Rewards[0].Amount[0].Amount) + ts.TxDualstakingClaimRewards(delegator, "") + + // add additional spec + spec1 := common.CreateMockSpec() + spec1.Index = "mock1" + spec1.Name = "mock1" + ts.AddSpec(spec1.Index, spec1) + err = ts.StakeProvider(providerAcc.GetVaultAddr(), provider, spec1, testStake) + makeProviderCommissionZero(ts, provider) + require.NoError(t, err) + + ts.AdvanceEpoch() + + relayPaymentMessage = sendRelay(ts, provider, clientAcc, []string{ts.spec.Index, spec1.Index}) + _, err = ts.TxPairingRelayPayment(relayPaymentMessage.Creator, relayPaymentMessage.Relays...) + require.Nil(ts.T, err) + + // advance month + blocksToSave + 1 to trigger the provider monthly payment + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + ts.AdvanceBlocks(ts.BlocksToSave() + 1) + + // delegator should get third of the payment + res, err = ts.QueryDualstakingDelegatorRewards(delegator, provider, "") + require.Nil(ts.T, err) + require.Equal(ts.T, ts.plan.Price.Amount.QuoRaw(3).AddRaw(1), res.Rewards[0].Amount[0].Amount) + ts.TxDualstakingClaimRewards(delegator, "") + + // move the stake between chains, should still get third + relayPaymentMessage = sendRelay(ts, provider, clientAcc, []string{ts.spec.Index, spec1.Index}) + _, err = ts.TxPairingRelayPayment(relayPaymentMessage.Creator, relayPaymentMessage.Relays...) + require.Nil(ts.T, err) + + err = ts.Keepers.Pairing.MoveProviderStake(ts.Ctx, provider, ts.spec.Index, spec1.Index, sdk.NewCoin(ts.BondDenom(), sdk.NewInt(testStake/10))) + require.Nil(ts.T, err) + + // advance month + blocksToSave + 1 to trigger the provider monthly payment + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + ts.AdvanceBlocks(ts.BlocksToSave() + 1) + + // delegator should get half of the payment + res, err = ts.QueryDualstakingDelegatorRewards(delegator, provider, "") + require.Nil(ts.T, err) + require.Equal(ts.T, ts.plan.Price.Amount.QuoRaw(3).AddRaw(1), res.Rewards[0].Amount[0].Amount) + ts.TxDualstakingClaimRewards(delegator, "") +} diff --git a/x/pairing/keeper/grpc_query_provider.go b/x/pairing/keeper/grpc_query_provider.go index e23711c1f8..bd2970c82c 100644 --- a/x/pairing/keeper/grpc_query_provider.go +++ b/x/pairing/keeper/grpc_query_provider.go @@ -17,9 +17,14 @@ func (k Keeper) Provider(goCtx context.Context, req *types.QueryProviderRequest) ctx := sdk.UnwrapSDKContext(goCtx) + metadata, err := k.epochStorageKeeper.GetMetadata(ctx, req.Address) + if err != nil { + return &types.QueryProviderResponse{}, nil + } + chains := []string{req.ChainID} if req.ChainID == "" { - chains = k.specKeeper.GetAllChainIDs(ctx) + chains = metadata.Chains } stakeEntries := []epochstoragetypes.StakeEntry{} @@ -28,7 +33,10 @@ func (k Keeper) Provider(goCtx context.Context, req *types.QueryProviderRequest) if !found { continue } - stakeEntry.Moniker = stakeEntry.Description.Moniker + stakeEntry.Description = metadata.Description + stakeEntry.Moniker = metadata.Description.Moniker + stakeEntry.DelegateCommission = metadata.DelegateCommission + stakeEntry.Vault = metadata.Vault stakeEntries = append(stakeEntries, stakeEntry) } diff --git a/x/pairing/keeper/grpc_query_provider_monthly_payout.go b/x/pairing/keeper/grpc_query_provider_monthly_payout.go index 7d5759fffa..1268a2bd82 100644 --- a/x/pairing/keeper/grpc_query_provider_monthly_payout.go +++ b/x/pairing/keeper/grpc_query_provider_monthly_payout.go @@ -73,7 +73,7 @@ func (k Keeper) ProviderMonthlyPayout(goCtx context.Context, req *types.QueryPro providerRewardAfterFees := sdk.NewCoins(sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), totalMonthlyReward.Sub(validatorsFee.AmountOf(denom)).Sub(communityFee.AmountOf(denom)))) // calculate only the provider reward - providerReward, _, err := k.dualstakingKeeper.RewardProvidersAndDelegators(ctx, req.Provider, chainID, providerRewardAfterFees, subsciptiontypes.ModuleName, true, true, true) + providerReward, err := k.dualstakingKeeper.RewardProvidersAndDelegators(ctx, req.Provider, chainID, providerRewardAfterFees, subsciptiontypes.ModuleName, true, true, true) if err != nil { return nil, err } diff --git a/x/pairing/keeper/grpc_query_providers.go b/x/pairing/keeper/grpc_query_providers.go index 182748f4f7..632f007311 100644 --- a/x/pairing/keeper/grpc_query_providers.go +++ b/x/pairing/keeper/grpc_query_providers.go @@ -19,16 +19,24 @@ func (k Keeper) Providers(goCtx context.Context, req *types.QueryProvidersReques stakeEntries := k.epochStorageKeeper.GetAllStakeEntriesCurrentForChainId(ctx, req.ChainID) - if !req.ShowFrozen { - stakeEntriesNoFrozen := []epochstoragetypes.StakeEntry{} - for i := range stakeEntries { - stakeEntries[i].Moniker = stakeEntries[i].Description.Moniker - - // show providers with valid stakeAppliedBlock (frozen providers have stakeAppliedBlock = MaxUint64) - if stakeEntries[i].GetStakeAppliedBlock() <= uint64(ctx.BlockHeight()) { - stakeEntriesNoFrozen = append(stakeEntriesNoFrozen, stakeEntries[i]) - } + stakeEntriesNoFrozen := []epochstoragetypes.StakeEntry{} + for i := range stakeEntries { + metadata, err := k.epochStorageKeeper.GetMetadata(ctx, stakeEntries[i].Address) + if err != nil { + return nil, err + } + stakeEntries[i].DelegateCommission = metadata.DelegateCommission + stakeEntries[i].Description = metadata.Description + stakeEntries[i].Moniker = metadata.Description.Moniker + stakeEntries[i].Vault = metadata.Vault + + // show providers with valid stakeAppliedBlock (frozen providers have stakeAppliedBlock = MaxUint64) + if !req.ShowFrozen && stakeEntries[i].GetStakeAppliedBlock() <= uint64(ctx.BlockHeight()) { + stakeEntriesNoFrozen = append(stakeEntriesNoFrozen, stakeEntries[i]) } + } + + if !req.ShowFrozen { stakeEntries = stakeEntriesNoFrozen } diff --git a/x/pairing/keeper/helpers_test.go b/x/pairing/keeper/helpers_test.go index e1509bd73b..78ca6c0360 100644 --- a/x/pairing/keeper/helpers_test.go +++ b/x/pairing/keeper/helpers_test.go @@ -73,24 +73,30 @@ func (ts *tester) addValidators(count int) { // addProvider: with default endpoints, geolocation, moniker func (ts *tester) addProvider(count int) error { d := common.MockDescription() - return ts.addProviderExtra(count, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) // default: endpoints, geolocation, moniker + return ts.addProviderExtra(count, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details, ts.spec) // default: endpoints, geolocation, moniker +} + +// addProvider: with default endpoints, geolocation, moniker +func (ts *tester) addProviderSpec(count int, spec string) error { + d := common.MockDescription() + return ts.addProviderExtra(count, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details, ts.Spec(spec)) } // addProviderGelocation: with geolocation, and default endpoints, moniker func (ts *tester) addProviderGeolocation(count int, geolocation int32) error { d := common.MockDescription() - return ts.addProviderExtra(count, nil, geolocation, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + return ts.addProviderExtra(count, nil, geolocation, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details, ts.spec) } // addProviderEndpoints: with endpoints, and default geolocation, moniker func (ts *tester) addProviderEndpoints(count int, endpoints []epochstoragetypes.Endpoint) error { d := common.MockDescription() - return ts.addProviderExtra(count, endpoints, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + return ts.addProviderExtra(count, endpoints, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details, ts.spec) } // addProviderDescription: with description, and default endpoints, geolocation func (ts *tester) addProviderDescription(count int, moniker string, identity string, website string, securityContact string, descriptionDetails string) error { - return ts.addProviderExtra(count, nil, 0, moniker, identity, website, securityContact, descriptionDetails) + return ts.addProviderExtra(count, nil, 0, moniker, identity, website, securityContact, descriptionDetails, ts.spec) } // addProviderExtra: with mock endpoints, and preset geolocation, description details @@ -103,11 +109,12 @@ func (ts *tester) addProviderExtra( website string, securityContact string, descriptionDetails string, + spec spectypes.Spec, ) error { start := len(ts.Accounts(common.PROVIDER)) for i := 0; i < count; i++ { acc, addr := ts.AddAccount(common.PROVIDER, start+i, testBalance) - err := ts.StakeProviderExtra(acc.GetVaultAddr(), addr, ts.spec, testStake, endpoints, geoloc, moniker, identity, website, securityContact, descriptionDetails) + err := ts.StakeProviderExtra(acc.GetVaultAddr(), addr, spec, testStake, endpoints, geoloc, moniker, identity, website, securityContact, descriptionDetails) if err != nil { return err } diff --git a/x/pairing/keeper/keeper.go b/x/pairing/keeper/keeper.go index 5451e3da5d..88642b77c0 100644 --- a/x/pairing/keeper/keeper.go +++ b/x/pairing/keeper/keeper.go @@ -4,7 +4,6 @@ import ( "fmt" storetypes "github.com/cosmos/cosmos-sdk/store/types" - epochstoragetypes "github.com/lavanet/lava/v3/x/epochstorage/types" timerstoretypes "github.com/lavanet/lava/v3/x/timerstore/types" "github.com/cometbft/cometbft/libs/log" @@ -35,8 +34,6 @@ type ( downtimeKeeper types.DowntimeKeeper dualstakingKeeper types.DualstakingKeeper stakingKeeper types.StakingKeeper - - pairingQueryCache *map[string][]epochstoragetypes.StakeEntry } ) @@ -74,8 +71,6 @@ func NewKeeper( ps = ps.WithKeyTable(types.ParamKeyTable()) } - emptypairingQueryCache := map[string][]epochstoragetypes.StakeEntry{} - keeper := &Keeper{ cdc: cdc, storeKey: storeKey, @@ -91,7 +86,6 @@ func NewKeeper( downtimeKeeper: downtimeKeeper, dualstakingKeeper: dualstakingKeeper, stakingKeeper: stakingKeeper, - pairingQueryCache: &emptypairingQueryCache, } // note that the timer and badgeUsedCu keys are the same (so we can use only the second arg) @@ -113,8 +107,6 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) BeginBlock(ctx sdk.Context) { if k.epochStorageKeeper.IsEpochStart(ctx) { - // reset pairing query cache every epoch - *k.pairingQueryCache = map[string][]epochstoragetypes.StakeEntry{} // remove old session payments k.RemoveOldEpochPayments(ctx) // unstake/jail unresponsive providers diff --git a/x/pairing/keeper/migrations.go b/x/pairing/keeper/migrations.go index 00b21034ce..d5f047be44 100644 --- a/x/pairing/keeper/migrations.go +++ b/x/pairing/keeper/migrations.go @@ -4,7 +4,9 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/lavanet/lava/v3/utils" + types1 "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/lavanet/lava/v3/utils/lavaslices" + epochstoragetypes "github.com/lavanet/lava/v3/x/epochstorage/types" v2 "github.com/lavanet/lava/v3/x/pairing/migrations/v2" ) @@ -24,33 +26,83 @@ func (m Migrator) MigrateVersion2To3(ctx sdk.Context) error { return nil } -// MigrateVersion3To4 fix delegation total in the stake entries -func (m Migrator) MigrateVersion3To4(ctx sdk.Context) error { +func (m Migrator) MigrateVersion4To5(ctx sdk.Context) error { entries := m.keeper.epochStorageKeeper.GetAllStakeEntriesCurrent(ctx) - epoch := m.keeper.epochStorageKeeper.GetCurrentNextEpoch(ctx) - for _, e := range entries { - delegations, err := m.keeper.dualstakingKeeper.GetProviderDelegators(ctx, e.Address, epoch) + providerMap := map[string][]*epochstoragetypes.StakeEntry{} + + // map the providers + for i := range entries { + providerMap[entries[i].Address] = append(providerMap[entries[i].Address], &entries[i]) + } + + // iterate over each provider + for address, entries := range providerMap { + metadata := epochstoragetypes.ProviderMetadata{Provider: address} + + // find the biggest vault + var biggestVault *epochstoragetypes.StakeEntry + biggestEntry := entries[0] + for i := range entries { + e := entries[i] + if biggestEntry.Stake.Amount.LT(e.Stake.Amount) { + biggestEntry = e + } + if e.Vault != e.Address { + if biggestVault == nil { + biggestVault = e + } else if biggestVault.Stake.Amount.LT(e.Stake.Amount) { + biggestVault = e + } + } + } + + // if no vault was found the vault is the address + metadata.Description = biggestEntry.Description + metadata.DelegateCommission = biggestEntry.DelegateCommission + metadata.Provider = address + if biggestVault != nil { + metadata.Vault = biggestVault.Vault + } else { + metadata.Vault = address + } + + // get all delegations and sum + delegations, err := m.keeper.dualstakingKeeper.GetProviderDelegators(ctx, metadata.Provider) if err != nil { - utils.LavaFormatError("failed getting provider delegators at MigrateVersion3To4", err, utils.LogAttr("provider", e.Address)) - continue + return err } - delegateTotal := sdk.ZeroInt() + metadata.TotalDelegations = sdk.NewCoin(m.keeper.stakingKeeper.BondDenom(ctx), sdk.ZeroInt()) for _, d := range delegations { - if e.Address == d.Delegator || e.Vault == d.Delegator || d.ChainID != e.Chain { - continue + if d.Delegator != metadata.Vault { + metadata.TotalDelegations = metadata.TotalDelegations.Add(d.Amount) } - delegateTotal = delegateTotal.Add(d.Amount.Amount) } - if !e.DelegateTotal.Amount.Equal(delegateTotal) { - fmt.Println("fixing delegate total for", e.Address, e.Chain) - e.DelegateTotal.Amount = delegateTotal - if e.TotalStake().LT(m.keeper.specKeeper.GetMinStake(ctx, e.Chain).Amount) { - e.Freeze() + // fix entries with different vaults + // count self delegations + TotalSelfDelegation := sdk.ZeroInt() + for _, e := range entries { + if e.Vault != metadata.Vault { + fmt.Println(address) + biggestVault.Stake = biggestVault.Stake.SubAmount(sdk.NewInt(1)) + e.Stake.Amount = sdk.OneInt() + e.Vault = metadata.Vault + } else { + TotalSelfDelegation = TotalSelfDelegation.Add(e.Stake.Amount) } - m.keeper.epochStorageKeeper.SetStakeEntryCurrent(ctx, e) } + + // calculate delegate total and update the entry + for _, entry := range entries { + metadata.Chains = lavaslices.AddUnique(metadata.Chains, entry.Chain) + entry.DelegateTotal = sdk.NewCoin(m.keeper.stakingKeeper.BondDenom(ctx), metadata.TotalDelegations.Amount.Mul(entry.Stake.Amount).Quo(TotalSelfDelegation)) + entry.Description = types1.Description{} + m.keeper.epochStorageKeeper.SetStakeEntryCurrent(ctx, *entry) + } + + // set the metadata + m.keeper.epochStorageKeeper.SetMetadata(ctx, metadata) } return nil diff --git a/x/pairing/keeper/migrations_test.go b/x/pairing/keeper/migrations_test.go new file mode 100644 index 0000000000..a5b7a5ee07 --- /dev/null +++ b/x/pairing/keeper/migrations_test.go @@ -0,0 +1,64 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/lavanet/lava/v3/testutil/common" + "github.com/lavanet/lava/v3/x/pairing/keeper" + "github.com/stretchr/testify/require" +) + +func TestMigration4To5(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 0, 3, 0) + + provider0, _ := ts.AddAccount(common.PROVIDER, 0, testBalance) + provider1, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) + + // stake provider 0 + d := common.MockDescription() + err := ts.StakeProviderExtra(provider0.GetVaultAddr(), provider0.Addr.String(), ts.Spec(SpecName(0)), testStake, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + + // stake provider 1 + err = ts.StakeProviderExtra(provider1.Addr.String(), provider1.Addr.String(), ts.Spec(SpecName(1)), testStake, nil, 0, "mon2", d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + + // mimic the state where one provider has vault and another dont + + entry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, SpecName(0), provider0.Addr.String()) + ts.Keepers.Epochstorage.RemoveStakeEntryCurrent(ts.Ctx, entry.Chain, entry.Address) + require.True(t, found) + + // provider 0 address is now provider1 with vault of provider 0 + entry.Address = provider1.Addr.String() + entry.DelegateCommission = 49 + entry.Description = d + ts.Keepers.Epochstorage.SetStakeEntryCurrent(ts.Ctx, entry) + + keeper.NewMigrator(ts.Keepers.Pairing).MigrateVersion4To5(ts.Ctx) + + // unbond from validator with provider1 + val, _ := ts.GetAccount(common.VALIDATOR, 0) + _, err = ts.TxUnbondValidator(provider1, val, math.NewInt(testStake)) + require.NoError(t, err) + + // check stake entries are correct + res, err := ts.QueryPairingProvider(provider1.Addr.String(), "") + require.NoError(t, err) + require.Len(t, res.StakeEntries, 2) + for _, entry := range res.StakeEntries { + require.Equal(t, entry.Address, provider1.Addr.String()) + require.Equal(t, entry.Vault, provider0.GetVaultAddr()) + } + + mds, err := ts.Keepers.Epochstorage.GetAllMetadata(ts.Ctx) + require.NoError(t, err) + require.Len(t, mds, 1) + require.Equal(t, provider1.Addr.String(), mds[0].Provider) + require.Equal(t, provider0.GetVaultAddr(), mds[0].Vault) + require.Equal(t, []string{SpecName(0), SpecName(1)}, mds[0].Chains) + require.Equal(t, uint64(49), mds[0].DelegateCommission) + require.Equal(t, d, mds[0].Description) +} diff --git a/x/pairing/keeper/msg_server_move_provider_stake.go b/x/pairing/keeper/msg_server_move_provider_stake.go new file mode 100644 index 0000000000..4eaa377492 --- /dev/null +++ b/x/pairing/keeper/msg_server_move_provider_stake.go @@ -0,0 +1,49 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v3/utils" + "github.com/lavanet/lava/v3/x/pairing/types" +) + +func (k msgServer) MoveProviderStake(goCtx context.Context, msg *types.MsgMoveProviderStake) (*types.MsgMoveProviderStakeResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + err := k.Keeper.MoveProviderStake(ctx, msg.Creator, msg.SrcChain, msg.DstChain, msg.Amount) + return &types.MsgMoveProviderStakeResponse{}, err +} + +func (k Keeper) MoveProviderStake(ctx sdk.Context, creator, srcChain, dstChain string, amount sdk.Coin) error { + srcEntry, found := k.epochStorageKeeper.GetStakeEntryCurrent(ctx, srcChain, creator) + if !found { + return utils.LavaFormatError("provider is not staked in the source chain", nil, + utils.LogAttr("provider", creator), + utils.LogAttr("chain", srcChain), + ) + } + + dstEntry, found := k.epochStorageKeeper.GetStakeEntryCurrent(ctx, dstChain, creator) + if !found { + return utils.LavaFormatError("provider is not staked in the destination chain", nil, + utils.LogAttr("provider", creator), + utils.LogAttr("chain", srcChain), + ) + } + + srcEntry.Stake = srcEntry.Stake.Sub(amount) + dstEntry.Stake = dstEntry.Stake.Add(amount) + + minSelfDelegation := k.dualstakingKeeper.MinSelfDelegation(ctx) + if srcEntry.Stake.IsLT(minSelfDelegation) { + return utils.LavaFormatError("provider will be below min stake on source chain", nil, + utils.LogAttr("provider", creator), + utils.LogAttr("chain", srcChain), + ) + } + + k.epochStorageKeeper.SetStakeEntryCurrent(ctx, srcEntry) + k.epochStorageKeeper.SetStakeEntryCurrent(ctx, dstEntry) + return k.dualstakingKeeper.AfterDelegationModified(ctx, srcEntry.Vault, srcEntry.Address, sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), sdk.ZeroInt()), false, true) +} diff --git a/x/pairing/keeper/msg_server_stake_provider_test.go b/x/pairing/keeper/msg_server_stake_provider_test.go index 161c020c57..d3046d47cd 100644 --- a/x/pairing/keeper/msg_server_stake_provider_test.go +++ b/x/pairing/keeper/msg_server_stake_provider_test.go @@ -27,9 +27,9 @@ func TestModifyStakeProviderWithDescription(t *testing.T) { providerAcct, providerAddr := ts.GetAccount(common.PROVIDER, 0) // Get the stake entry and check the provider is staked - stakeEntry, foundProvider := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, providerAddr) - require.True(t, foundProvider) - require.True(t, stakeEntry.Description.Equal(common.MockDescription())) + stakeEntry, err := ts.QueryPairingProvider(providerAddr, ts.spec.Index) + require.NoError(t, err) + require.True(t, stakeEntry.StakeEntries[0].Description.Equal(common.MockDescription())) // modify description dNew := stakingtypes.NewDescription("bla", "blan", "bal", "lala", "asdasd") @@ -38,9 +38,9 @@ func TestModifyStakeProviderWithDescription(t *testing.T) { ts.AdvanceEpoch() // Get the stake entry and check the provider is staked - stakeEntry, foundProvider = ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, providerAddr) - require.True(t, foundProvider) - require.True(t, stakeEntry.Description.Equal(dNew)) + stakeEntry, err = ts.QueryPairingProvider(providerAddr, ts.spec.Index) + require.NoError(t, err) + require.True(t, stakeEntry.StakeEntries[0].Description.Equal(dNew)) } func TestCmdStakeProviderGeoConfigAndEnum(t *testing.T) { @@ -207,7 +207,7 @@ func TestCmdStakeProviderGeoConfigAndEnum(t *testing.T) { endpoints[i].ApiInterfaces = []string{"stub"} endpoints[i].Addons = []string{} } - _, err = ts.TxPairingStakeProvider(provider, acc.GetVaultAddr(), ts.spec.Index, ts.spec.MinStakeProvider, endpoints, geo, common.MockDescription()) + _, err = ts.TxPairingStakeProvider(provider, acc.GetVaultAddr(), ts.spec.Index, ts.spec.MinStakeProvider, endpoints, geo, common.MockDescription(), 100) require.NoError(t, err) } else { require.Error(t, err) @@ -657,7 +657,7 @@ func TestStakeEndpoints(t *testing.T) { for _, play := range playbook { t.Run(play.name, func(t *testing.T) { - _, err := ts.TxPairingStakeProvider(providerAddr, providerAcc.GetVaultAddr(), ts.spec.Index, amount, play.endpoints, play.geolocation, common.MockDescription()) + _, err := ts.TxPairingStakeProvider(providerAddr, providerAcc.GetVaultAddr(), ts.spec.Index, amount, play.endpoints, play.geolocation, common.MockDescription(), 100) if play.success { require.NoError(t, err) @@ -761,7 +761,7 @@ func TestUnfreezeWithDelegations(t *testing.T) { // add delegator and delegate to provider so its effective stake is MinStakeProvider+MinSelfDelegation+1 // provider should still be frozen _, consumer := ts.AddAccount(common.CONSUMER, 1, testBalance) - _, err = ts.TxDualstakingDelegate(consumer, provider, ts.spec.Index, ts.spec.MinStakeProvider) + _, err = ts.TxDualstakingDelegate(consumer, provider, ts.spec.MinStakeProvider) require.NoError(t, err) ts.AdvanceEpoch() // apply delegation stakeEntry, found = ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, provider) @@ -801,7 +801,7 @@ func TestCommisionChange(t *testing.T) { // add delegator and delegate to provider _, consumer := ts.AddAccount(common.CONSUMER, 1, testBalance) - _, err = ts.TxDualstakingDelegate(consumer, provider, ts.spec.Index, ts.spec.MinStakeProvider) + _, err = ts.TxDualstakingDelegate(consumer, provider, ts.spec.MinStakeProvider) require.NoError(t, err) ts.AdvanceEpoch() // apply delegation ts.AdvanceBlock(time.Hour * 25) // advance time to allow changes @@ -855,11 +855,12 @@ func TestVaultProviderNewStakeEntry(t *testing.T) { provider sdk.AccAddress spec spectypes.Spec valid bool + fail bool }{ - {"stake provider = vault", p1Acc.Addr, p1Acc.Addr, ts.spec, true}, - {"stake provider != vault", p2Acc.Vault.Addr, p2Acc.Addr, ts.spec, true}, - {"stake existing provider", p1Acc.Vault.Addr, p1Acc.Addr, ts.spec, false}, - {"stake existing provider different chain", p1Acc.Vault.Addr, p1Acc.Addr, spec1, true}, + {"stake provider = vault", p1Acc.Addr, p1Acc.Addr, ts.spec, true, false}, + {"stake provider != vault", p2Acc.Vault.Addr, p2Acc.Addr, ts.spec, true, false}, + {"stake existing provider", p1Acc.Vault.Addr, p1Acc.Addr, ts.spec, false, false}, + {"stake existing provider different chain", p1Acc.Vault.Addr, p1Acc.Addr, spec1, false, true}, } for _, tt := range tests { @@ -881,10 +882,14 @@ func TestVaultProviderNewStakeEntry(t *testing.T) { // stake entry _, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, tt.spec.Index, tt.provider.String()) + if tt.fail { + require.False(t, found) + return + } require.True(t, found) // should be found because provider is registered in a vaild stake entry // delegations - res, err := ts.QueryDualstakingDelegatorProviders(tt.vault.String(), false) + res, err := ts.QueryDualstakingDelegatorProviders(tt.vault.String()) require.NoError(t, err) require.Len(t, res.Delegations, 0) } else { @@ -905,7 +910,7 @@ func TestVaultProviderNewStakeEntry(t *testing.T) { require.Equal(t, int64(0), stakeEntry.DelegateTotal.Amount.Int64()) // delegations - res, err := ts.QueryDualstakingDelegatorProviders(tt.vault.String(), false) + res, err := ts.QueryDualstakingDelegatorProviders(tt.vault.String()) require.NoError(t, err) require.Len(t, res.Delegations, 1) require.Equal(t, tt.provider.String(), res.Delegations[0].Provider) @@ -963,7 +968,7 @@ func TestVaultProviderExistingStakeEntry(t *testing.T) { require.True(t, found) // should be found because provider is registered in a vaild stake entry // delegations - res, err := ts.QueryDualstakingDelegatorProviders(tt.vault.String(), false) + res, err := ts.QueryDualstakingDelegatorProviders(tt.vault.String()) require.NoError(t, err) require.Len(t, res.Delegations, 0) } else { @@ -984,7 +989,7 @@ func TestVaultProviderExistingStakeEntry(t *testing.T) { require.Equal(t, int64(0), stakeEntry.DelegateTotal.Amount.Int64()) // delegations - res, err := ts.QueryDualstakingDelegatorProviders(tt.vault.String(), false) + res, err := ts.QueryDualstakingDelegatorProviders(tt.vault.String()) require.NoError(t, err) require.Len(t, res.Delegations, 1) require.Equal(t, tt.provider.String(), res.Delegations[0].Provider) @@ -1008,11 +1013,13 @@ func TestVaultProviderModifyStakeEntry(t *testing.T) { provider := acc.Addr.String() vault := acc.GetVaultAddr() - valAcc, _ := ts.GetAccount(common.VALIDATOR, 0) stakeEntry, found := ts.Keepers.Epochstorage.GetStakeEntryCurrent(ts.Ctx, ts.spec.Index, acc.Addr.String()) require.True(t, found) + metadata, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider) + require.NoError(t, err) + // consts for stake entry changes const ( STAKE = iota + 1 @@ -1047,12 +1054,11 @@ func TestVaultProviderModifyStakeEntry(t *testing.T) { t.Run(tt.name, func(t *testing.T) { msg := types.MsgStakeProvider{ Creator: tt.creator, - Validator: sdk.ValAddress(valAcc.Addr).String(), ChainID: stakeEntry.Chain, Amount: stakeEntry.Stake, Geolocation: stakeEntry.Geolocation, Endpoints: stakeEntry.Endpoints, - DelegateCommission: stakeEntry.DelegateCommission, + DelegateCommission: metadata.DelegateCommission, Address: stakeEntry.Address, Description: stakeEntry.Description, } @@ -1126,7 +1132,7 @@ func TestDelegatorStakesAfterProviderUnstakes(t *testing.T) { // create delegator and delegate to provider _, delegator := ts.AddAccount(common.CONSUMER, 0, testBalance) - _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, common.NewCoin(ts.TokenDenom(), testBalance/4)) + _, err := ts.TxDualstakingDelegate(delegator, provider, common.NewCoin(ts.TokenDenom(), testBalance/4)) require.NoError(t, err) ts.AdvanceEpoch() @@ -1134,7 +1140,7 @@ func TestDelegatorStakesAfterProviderUnstakes(t *testing.T) { _, err = ts.TxPairingUnstakeProvider(vault, ts.spec.Index) require.NoError(t, err) - res, err := ts.QueryDualstakingDelegatorProviders(delegator, false) + res, err := ts.QueryDualstakingDelegatorProviders(delegator) require.NoError(t, err) require.Len(t, res.Delegations, 1) require.Equal(t, delegator, res.Delegations[0].Delegator) @@ -1182,7 +1188,7 @@ func TestDelegatorAfterProviderUnstakeAndStake(t *testing.T) { // create apple apple and delegate to banana _, apple := ts.AddAccount(common.CONSUMER, 0, testBalance) - _, err = ts.TxDualstakingDelegate(apple, banana, ts.spec.Index, common.NewCoin(ts.TokenDenom(), testBalance/4)) + _, err = ts.TxDualstakingDelegate(apple, banana, common.NewCoin(ts.TokenDenom(), testBalance/4)) require.NoError(t, err) ts.AdvanceEpoch() @@ -1191,7 +1197,7 @@ func TestDelegatorAfterProviderUnstakeAndStake(t *testing.T) { require.NoError(t, err) ts.AdvanceEpoch() - res, err := ts.QueryDualstakingDelegatorProviders(apple, false) + res, err := ts.QueryDualstakingDelegatorProviders(apple) require.NoError(t, err) require.Len(t, res.Delegations, 1) require.Equal(t, apple, res.Delegations[0].Delegator) @@ -1211,10 +1217,10 @@ func TestDelegatorAfterProviderUnstakeAndStake(t *testing.T) { require.True(t, found) require.NotZero(t, stakeEntry.DelegateTotal.Amount.Int64()) - _, err = ts.TxDualstakingUnbond(apple, banana, ts.spec.Index, common.NewCoin(ts.TokenDenom(), testBalance/4)) + _, err = ts.TxDualstakingUnbond(apple, banana, common.NewCoin(ts.TokenDenom(), testBalance/4)) require.NoError(t, err) ts.AdvanceEpoch() - res, err := ts.QueryDualstakingProviderDelegators(banana, false) + res, err := ts.QueryDualstakingProviderDelegators(banana) require.NoError(t, err) require.Len(t, res.Delegations, 1) require.Equal(t, banana, res.Delegations[0].Provider) @@ -1229,10 +1235,10 @@ func TestDelegatorAfterProviderUnstakeAndStake(t *testing.T) { require.True(t, found) require.Zero(t, stakeEntry.DelegateTotal.Amount.Int64()) - _, err = ts.TxDualstakingUnbond(apple, banana, ts.spec.Index, common.NewCoin(ts.TokenDenom(), testBalance/4)) + _, err = ts.TxDualstakingUnbond(apple, banana, common.NewCoin(ts.TokenDenom(), testBalance/4)) require.NoError(t, err) ts.AdvanceEpoch() - res, err := ts.QueryDualstakingProviderDelegators(banana, false) + res, err := ts.QueryDualstakingProviderDelegators(banana) require.NoError(t, err) require.Len(t, res.Delegations, 0) } diff --git a/x/pairing/keeper/msg_server_unstake_provider_test.go b/x/pairing/keeper/msg_server_unstake_provider_test.go index 296dfd4b5b..3bcff8ea12 100644 --- a/x/pairing/keeper/msg_server_unstake_provider_test.go +++ b/x/pairing/keeper/msg_server_unstake_provider_test.go @@ -10,32 +10,31 @@ import ( // TestVaultProviderUnstake tests that only the vault address can unstake. // Scenarios: // 1. unstake with vault -> should work -// 2. try with provider -> should fail +// 2. try with provider -> should work func TestVaultProviderUnstake(t *testing.T) { ts := newTester(t) - ts.setupForPayments(1, 0, 0) + ts.setupForPayments(2, 0, 0) - acc, _ := ts.GetAccount(common.PROVIDER, 0) - provider := acc.Addr.String() - vault := acc.GetVaultAddr() + acc1, _ := ts.GetAccount(common.PROVIDER, 0) + provider1 := acc1.Addr.String() + + acc2, _ := ts.GetAccount(common.PROVIDER, 1) + vault2 := acc2.GetVaultAddr() tests := []struct { name string creator string - valid bool }{ - {"provider unstakes", provider, false}, - {"vault unstakes", vault, true}, + {"provider unstakes", provider1}, + {"vault unstakes", vault2}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + prov := ts.Keepers.Epochstorage.GetAllStakeEntriesCurrentForChainId(ts.Ctx, ts.spec.Index) + _ = prov _, err := ts.TxPairingUnstakeProvider(tt.creator, ts.spec.Index) - if tt.valid { - require.NoError(t, err) - } else { - require.Error(t, err) - } + require.NoError(t, err) }) } } diff --git a/x/pairing/keeper/pairing.go b/x/pairing/keeper/pairing.go index 023eae047d..638c01648a 100644 --- a/x/pairing/keeper/pairing.go +++ b/x/pairing/keeper/pairing.go @@ -80,7 +80,7 @@ func (k Keeper) GetPairingForClient(ctx sdk.Context, chainID string, clientAddre return nil, fmt.Errorf("invalid user for pairing: %s", err.Error()) } - providers, _, _, err = k.getPairingForClient(ctx, chainID, block, strictestPolicy, cluster, project.Index, false, true) + providers, _, _, err = k.getPairingForClient(ctx, chainID, block, strictestPolicy, cluster, project.Index, false) return providers, err } @@ -90,7 +90,7 @@ func (k Keeper) CalculatePairingChance(ctx sdk.Context, provider string, chainID totalScore := cosmosmath.ZeroUint() providerScore := cosmosmath.ZeroUint() - _, _, scores, err := k.getPairingForClient(ctx, chainID, uint64(ctx.BlockHeight()), policy, cluster, "dummy", true, false) + _, _, scores, err := k.getPairingForClient(ctx, chainID, uint64(ctx.BlockHeight()), policy, cluster, "dummy", true) if err != nil { return cosmosmath.LegacyZeroDec(), err } @@ -117,22 +117,12 @@ func (k Keeper) CalculatePairingChance(ctx sdk.Context, provider string, chainID // function used to get a new pairing from provider and client // first argument has all metadata, second argument is only the addresses -// useCache is a boolean argument that is used to determine whether pairing cache should be used -// Note: useCache should only be true for queries! functions that write to the state and use this function should never put useCache=true -func (k Keeper) getPairingForClient(ctx sdk.Context, chainID string, block uint64, policy *planstypes.Policy, cluster string, projectIndex string, calcChance bool, useCache bool) (providers []epochstoragetypes.StakeEntry, allowedCU uint64, providerScores []*pairingscores.PairingScore, errorRet error) { +func (k Keeper) getPairingForClient(ctx sdk.Context, chainID string, block uint64, policy *planstypes.Policy, cluster string, projectIndex string, calcChance bool) (providers []epochstoragetypes.StakeEntry, allowedCU uint64, providerScores []*pairingscores.PairingScore, errorRet error) { epoch, providersType, err := k.VerifyPairingData(ctx, chainID, block) if err != nil { return nil, 0, nil, fmt.Errorf("invalid pairing data: %s", err) } - // to be used only in queries as this changes gas calculations, and therefore must not be part of consensus - if useCache { - providers, found := k.GetPairingQueryCache(projectIndex, chainID, epoch) - if found { - return providers, policy.EpochCuLimit, nil, nil - } - } - stakeEntries := k.epochStorageKeeper.GetAllStakeEntriesForEpochChainId(ctx, epoch, chainID) if len(stakeEntries) == 0 { return nil, 0, nil, fmt.Errorf("did not find providers for pairing: epoch:%d, chainID: %s", block, chainID) @@ -149,9 +139,6 @@ func (k Keeper) getPairingForClient(ctx sdk.Context, chainID string, block uint6 stakeEntriesFiltered = append(stakeEntriesFiltered, stakeEntries[i]) } } - if useCache { - k.SetPairingQueryCache(projectIndex, chainID, epoch, stakeEntriesFiltered) - } return stakeEntriesFiltered, policy.EpochCuLimit, nil, nil } @@ -171,9 +158,6 @@ func (k Keeper) getPairingForClient(ctx sdk.Context, chainID string, block uint6 for _, score := range providerScores { filteredEntries = append(filteredEntries, *score.Provider) } - if useCache { - k.SetPairingQueryCache(projectIndex, chainID, epoch, filteredEntries) - } return filteredEntries, policy.EpochCuLimit, nil, nil } @@ -194,10 +178,6 @@ func (k Keeper) getPairingForClient(ctx sdk.Context, chainID string, block uint6 prevGroupSlot = group } - if useCache { - k.SetPairingQueryCache(projectIndex, chainID, epoch, providers) - } - return providers, policy.EpochCuLimit, providerScores, nil } @@ -350,7 +330,7 @@ func (k Keeper) ValidatePairingForClient(ctx sdk.Context, chainID string, provid return false, allowedCU, []epochstoragetypes.StakeEntry{}, fmt.Errorf("invalid user for pairing: %s", err.Error()) } - validAddresses, allowedCU, _, err = k.getPairingForClient(ctx, chainID, epoch, strictestPolicy, cluster, project.Index, false, false) + validAddresses, allowedCU, _, err = k.getPairingForClient(ctx, chainID, epoch, strictestPolicy, cluster, project.Index, false) if err != nil { return false, allowedCU, []epochstoragetypes.StakeEntry{}, err } @@ -363,7 +343,7 @@ func (k Keeper) ValidatePairingForClient(ctx sdk.Context, chainID string, provid utils.LavaFormatPanic("critical: invalid provider address for payment", err, utils.Attribute{Key: "chainID", Value: chainID}, utils.Attribute{Key: "client", Value: project.Subscription}, - utils.Attribute{Key: "provider", Value: providerAccAddr.String()}, + utils.Attribute{Key: "provider", Value: possibleAddr.Address}, utils.Attribute{Key: "epochBlock", Value: strconv.FormatUint(epoch, 10)}, ) } diff --git a/x/pairing/keeper/pairing_cache.go b/x/pairing/keeper/pairing_cache.go index 5cad220cd6..a3652babc8 100644 --- a/x/pairing/keeper/pairing_cache.go +++ b/x/pairing/keeper/pairing_cache.go @@ -38,27 +38,3 @@ func (k Keeper) ResetPairingRelayCache(ctx sdk.Context) { store.Delete(iterator.Key()) } } - -// the cache used for the query, does not write into state -func (k Keeper) SetPairingQueryCache(project string, chainID string, epoch uint64, pairedProviders []epochstoragetypes.StakeEntry) { - if k.pairingQueryCache == nil { - // pairing cache is not initialized, will be in next epoch so simply skip - return - } - key := types.NewPairingCacheKey(project, chainID, epoch) - - (*k.pairingQueryCache)[key] = pairedProviders -} - -func (k Keeper) GetPairingQueryCache(project string, chainID string, epoch uint64) ([]epochstoragetypes.StakeEntry, bool) { - if k.pairingQueryCache == nil { - // pairing cache is not initialized, will be in next epoch so simply skip - return nil, false - } - key := types.NewPairingCacheKey(project, chainID, epoch) - if providers, ok := (*k.pairingQueryCache)[key]; ok { - return providers, true - } - - return nil, false -} diff --git a/x/pairing/keeper/pairing_cache_test.go b/x/pairing/keeper/pairing_cache_test.go index 31b6669192..fb1f0829ef 100644 --- a/x/pairing/keeper/pairing_cache_test.go +++ b/x/pairing/keeper/pairing_cache_test.go @@ -7,44 +7,7 @@ import ( "github.com/stretchr/testify/require" ) -// TestPairingQueryCache tests the following: -// 1. The pairing query cache is reset every epoch -// 2. Getting pairing with a query using an existent cache entry consumes fewer gas than without one -func TestPairingQueryCache(t *testing.T) { - ts := newTester(t) - ts.setupForPayments(1, 1, 0) // 1 provider, 1 client, default providers-to-pair - - _, consumer := ts.GetAccount(common.CONSUMER, 0) - - getPairingGas := func(ts *tester) uint64 { - gm := ts.Ctx.GasMeter() - before := gm.GasConsumed() - _, err := ts.QueryPairingGetPairing(ts.spec.Index, consumer) - require.NoError(t, err) - return gm.GasConsumed() - before - } - - // query for pairing for the first time - empty cache - emptyCacheGas := getPairingGas(ts) - - // query for pairing for the second time - non-empty cache - filledCacheGas := getPairingGas(ts) - - // second time gas should be smaller than first time - require.Less(t, filledCacheGas, emptyCacheGas) - - // advance block to test it stays the same (should still be less than empty cache gas) - ts.AdvanceBlock() - filledAfterBlockCacheGas := getPairingGas(ts) - require.Less(t, filledAfterBlockCacheGas, emptyCacheGas) - - // advance epoch to reset the cache - ts.AdvanceEpoch() - emptyCacheAgainGas := getPairingGas(ts) - require.Equal(t, emptyCacheGas, emptyCacheAgainGas) -} - -// TestPairingQueryCache tests the following: +// TestPairingRelayCache tests the following: // 1. The pairing relay cache is reset every block // 2. Getting pairing in relay payment using an existent cache entry consumes fewer gas than without one func TestPairingRelayCache(t *testing.T) { diff --git a/x/pairing/keeper/pairing_test.go b/x/pairing/keeper/pairing_test.go index f1e607aff0..55e1024b95 100644 --- a/x/pairing/keeper/pairing_test.go +++ b/x/pairing/keeper/pairing_test.go @@ -968,7 +968,7 @@ func TestPairingDistributionPerStake(t *testing.T) { // double the stake of the first provider p := allProviders.StakeEntry[0] - _, err = ts.TxDualstakingDelegate(p.Vault, p.Address, ts.spec.Index, p.Stake) + _, err = ts.TxDualstakingDelegate(p.Vault, p.Address, p.Stake) require.NoError(t, err) ts.AdvanceEpoch() diff --git a/x/pairing/keeper/scores/pairing_slot.go b/x/pairing/keeper/scores/pairing_slot.go index 5d6935f56e..fe13eaff63 100644 --- a/x/pairing/keeper/scores/pairing_slot.go +++ b/x/pairing/keeper/scores/pairing_slot.go @@ -36,7 +36,7 @@ func (psg PairingSlotGroup) Subtract(other *PairingSlotGroup) *PairingSlot { otherReq, found := other.Reqs[key] if !found { reqsDiff[key] = req - } else if !req.Equal(otherReq) { + } else if req != nil && !req.Equal(otherReq) { reqsDiff[key] = req } } diff --git a/x/pairing/keeper/scores/stake_req.go b/x/pairing/keeper/scores/stake_req.go index 44eb3ff5c0..74af1713fb 100644 --- a/x/pairing/keeper/scores/stake_req.go +++ b/x/pairing/keeper/scores/stake_req.go @@ -17,6 +17,9 @@ func (sr *StakeReq) Init(policy planstypes.Policy) bool { // Score calculates the the provider score as the normalized stake func (sr *StakeReq) Score(score PairingScore) math.Uint { + if sr == nil { + return math.OneUint() + } effectiveStake := score.Provider.TotalStake() if !effectiveStake.IsPositive() { return math.OneUint() diff --git a/x/pairing/keeper/single_provider_test.go b/x/pairing/keeper/single_provider_test.go new file mode 100644 index 0000000000..29c33cd6c9 --- /dev/null +++ b/x/pairing/keeper/single_provider_test.go @@ -0,0 +1,510 @@ +package keeper_test + +import ( + "strconv" + "testing" + + "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v3/testutil/common" + "github.com/lavanet/lava/v3/x/pairing/client/cli" + pairingtypes "github.com/lavanet/lava/v3/x/pairing/types" + "github.com/stretchr/testify/require" +) + +func SpecName(num int) string { + return "spec" + strconv.Itoa(num) +} + +func SetupForSingleProviderTests(ts *tester, providers, specs, clientsCount int) { + for i := 0; i < providers; i++ { + ts.AddAccount(common.PROVIDER, i, 100*testBalance) + } + + for i := 0; i < specs; i++ { + spec := ts.spec + spec.Index = SpecName(i) + ts.AddSpec(spec.Index, spec) + for i := 0; i < providers; i++ { + acc, addr := ts.GetAccount(common.PROVIDER, i) + d := common.MockDescription() + err := ts.StakeProviderExtra(acc.GetVaultAddr(), addr, spec, testStake, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + } + } + + ts.addClient(clientsCount) + ts.AdvanceEpoch() +} + +// * unstake to see the delegations distributions +// * unstake from the rest of the chains +// * stake to get all delegations back +func TestUnstakeStake(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 1, 5, 0) + + delegator, _ := ts.AddAccount("del", 1, 1000000000) + provider0, _ := ts.GetAccount(common.PROVIDER, 0) + + // delegate and check delegatetotal + _, err := ts.TxDualstakingDelegate(delegator.Addr.String(), provider0.Addr.String(), types.NewInt64Coin(ts.TokenDenom(), 5000)) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(1000), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + ts.AdvanceEpoch() + + stakeEntries := ts.Keepers.Epochstorage.GetAllStakeEntriesForEpoch(ts.Ctx, ts.EpochStart()) + require.Len(t, stakeEntries, 5) + for _, entry := range stakeEntries { + require.Equal(t, int64(1000), entry.DelegateTotal.Amount.Int64()) + } + + // unstake spec0 provider + _, err = ts.TxPairingUnstakeProvider(provider0.GetVaultAddr(), SpecName(0)) + require.NoError(t, err) + + // check redistribution of delegation + for i := 1; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(1250), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + // unstake all + for i := 1; i < 5; i++ { + _, err = ts.TxPairingUnstakeProvider(provider0.GetVaultAddr(), SpecName(i)) + require.NoError(t, err) + } + + res, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String()) + require.NoError(t, err) + require.Len(t, res.Delegations, 1) + require.Equal(t, provider0.Addr.String(), res.Delegations[0].Provider) + require.Equal(t, int64(5000), res.Delegations[0].Amount.Amount.Int64()) + + // stake again and check we got the delegation back + d := common.MockDescription() + err = ts.StakeProviderExtra(provider0.GetVaultAddr(), provider0.Addr.String(), ts.Spec(SpecName(0)), testStake, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + + res1, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(0)) + require.NoError(t, err) + require.Equal(t, int64(5000), res1.StakeEntries[0].DelegateTotal.Amount.Int64()) + + // stake again on spec1 and check delegations + err = ts.StakeProviderExtra(provider0.GetVaultAddr(), provider0.Addr.String(), ts.Spec(SpecName(1)), testStake, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + + res1, err = ts.QueryPairingProvider(provider0.Addr.String(), SpecName(0)) + require.NoError(t, err) + require.Equal(t, int64(2500), res1.StakeEntries[0].DelegateTotal.Amount.Int64()) + + res1, err = ts.QueryPairingProvider(provider0.Addr.String(), SpecName(1)) + require.NoError(t, err) + require.Equal(t, int64(2500), res1.StakeEntries[0].DelegateTotal.Amount.Int64()) + + ts.AdvanceEpoch() + + stakeEntries = ts.Keepers.Epochstorage.GetAllStakeEntriesForEpoch(ts.Ctx, ts.EpochStart()) + require.Len(t, stakeEntries, 2) + for _, entry := range stakeEntries { + require.Equal(t, int64(2500), entry.DelegateTotal.Amount.Int64()) + } +} + +// * unstake to see the delegations distributions +// * unstake from the rest of the chains +// * stake to get all delegations back with a new vault +func TestUnstakeStakeNewVault(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 1, 5, 0) + + delegator, _ := ts.AddAccount("del", 1, 1000000000) + provider0, _ := ts.GetAccount(common.PROVIDER, 0) + provider1, _ := ts.AddAccount(common.PROVIDER, 1, 1000000000) + + // delegate and check delegatetotal + _, err := ts.TxDualstakingDelegate(delegator.Addr.String(), provider0.Addr.String(), types.NewInt64Coin(ts.TokenDenom(), 5000)) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(1000), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + ts.AdvanceEpoch() + + for i := 0; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(1000), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + // unstake all + for i := 0; i < 5; i++ { + _, err = ts.TxPairingUnstakeProvider(provider0.GetVaultAddr(), SpecName(i)) + require.NoError(t, err) + + md, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider0.Addr.String()) + if i == 4 { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Len(t, md.Chains, 4-i) + } + } + + res, err := ts.QueryDualstakingDelegatorProviders(delegator.Addr.String()) + require.NoError(t, err) + require.Len(t, res.Delegations, 1) + require.Equal(t, provider0.Addr.String(), res.Delegations[0].Provider) + require.Equal(t, int64(5000), res.Delegations[0].Amount.Amount.Int64()) + + // stake again and check we got the delegation back + d := common.MockDescription() + err = ts.StakeProviderExtra(provider1.GetVaultAddr(), provider0.Addr.String(), ts.Spec(SpecName(0)), testStake, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + + res1, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(0)) + require.NoError(t, err) + require.Equal(t, int64(5000), res1.StakeEntries[0].DelegateTotal.Amount.Int64()) + + // stake again on spec1 and check delegations + err = ts.StakeProviderExtra(provider1.GetVaultAddr(), provider0.Addr.String(), ts.Spec(SpecName(1)), testStake, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + + res1, err = ts.QueryPairingProvider(provider0.Addr.String(), SpecName(0)) + require.NoError(t, err) + require.Equal(t, int64(2500), res1.StakeEntries[0].DelegateTotal.Amount.Int64()) + + res1, err = ts.QueryPairingProvider(provider0.Addr.String(), SpecName(1)) + require.NoError(t, err) + require.Equal(t, int64(2500), res1.StakeEntries[0].DelegateTotal.Amount.Int64()) +} + +func TestDelegations(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 1, 5, 0) + + delegator, _ := ts.AddAccount("del", 1, 1000000000) + provider, _ := ts.GetAccount(common.PROVIDER, 0) + + // delegate amount that does not divide by 5 + _, err := ts.TxDualstakingDelegate(delegator.Addr.String(), provider.Addr.String(), types.NewInt64Coin(ts.TokenDenom(), 4999)) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(999), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + // unbond 4 tokens, now amount divides by 5 + _, err = ts.TxDualstakingUnbond(delegator.Addr.String(), provider.Addr.String(), types.NewInt64Coin(ts.TokenDenom(), 4)) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(999), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + // add stake to spec0 + d := common.MockDescription() + err = ts.StakeProviderExtra(provider.GetVaultAddr(), provider.Addr.String(), ts.Spec(SpecName(0)), 2*testStake, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + + // check delegate total is twice than others in spec0 + res, err := ts.QueryPairingProvider(provider.Addr.String(), SpecName(0)) + require.NoError(t, err) + require.Equal(t, int64(1665), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + + for i := 1; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(832), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + // unbond with vault, should be uniformali + _, err = ts.TxDualstakingUnbond(provider.GetVaultAddr(), provider.Addr.String(), types.NewInt64Coin(ts.TokenDenom(), testStake)) + require.NoError(t, err) + + res, err = ts.QueryPairingProvider(provider.Addr.String(), SpecName(0)) + require.NoError(t, err) + require.Equal(t, testStake+testStake*4/5, res.StakeEntries[0].Stake.Amount.Int64()) + require.Equal(t, 4995*res.StakeEntries[0].Stake.Amount.Int64()/(5*testStake), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + + for i := 1; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, testStake*4/5, res.StakeEntries[0].Stake.Amount.Int64()) + require.Equal(t, 4995*res.StakeEntries[0].Stake.Amount.Int64()/(5*testStake), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + // unbond all delegator + _, err = ts.TxDualstakingUnbond(delegator.Addr.String(), provider.Addr.String(), types.NewInt64Coin(ts.TokenDenom(), 4995)) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(0), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } +} + +func TestUnstakeWithOperator(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 1, 5, 0) + + provider, _ := ts.GetAccount(common.PROVIDER, 0) + + // unstake one chain + _, err := ts.TxPairingUnstakeProvider(provider.Addr.String(), SpecName(0)) + require.NoError(t, err) + + // check the entries got the stake of spec0 + for i := 1; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, testStake*5/4, res.StakeEntries[0].Stake.Amount.Int64()) + } + + // unstake everything + for i := 1; i < 5; i++ { + _, err := ts.TxPairingUnstakeProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + } + + res, err := ts.QueryPairingProvider(provider.Addr.String(), "") + require.NoError(t, err) + require.Len(t, res.StakeEntries, 0) + + // stake again + d := common.MockDescription() + err = ts.StakeProviderExtra(provider.GetVaultAddr(), provider.Addr.String(), ts.Spec(SpecName(0)), 1, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details) + require.NoError(ts.T, err) + + // check we got all the self delegations + res, err = ts.QueryPairingProvider(provider.Addr.String(), SpecName(0)) + require.NoError(t, err) + require.Equal(t, testStake*5+1, res.StakeEntries[0].Stake.Amount.Int64()) +} + +func TestChangeCommisionAndDescription(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 0, 5, 0) + provider, _ := ts.AddAccount(common.PROVIDER, 0, testBalance) + + d := common.MockDescription() + err := ts.StakeProviderFull(provider.GetVaultAddr(), provider.Addr.String(), ts.Spec(SpecName(0)), 100, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details, 50) + require.NoError(ts.T, err) + + md, err := ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider.Addr.String()) + require.NoError(ts.T, err) + require.Equal(t, d, md.Description) + require.Equal(t, md.DelegateCommission, uint64(50)) + + d.Moniker = "test2" + err = ts.StakeProviderFull(provider.GetVaultAddr(), provider.Addr.String(), ts.Spec(SpecName(1)), 100, nil, 0, d.Moniker, d.Identity, d.Website, d.SecurityContact, d.Details, 49) + require.NoError(ts.T, err) + + md, err = ts.Keepers.Epochstorage.GetMetadata(ts.Ctx, provider.Addr.String()) + require.NoError(ts.T, err) + require.Equal(t, d, md.Description) + require.Equal(t, md.DelegateCommission, uint64(49)) + + res, err := ts.QueryPairingProviders(ts.Spec(SpecName(0)).Index, true) + require.NoError(ts.T, err) + require.Equal(t, d, res.StakeEntry[0].Description) + require.Equal(t, uint64(49), res.StakeEntry[0].DelegateCommission) + + res1, err := ts.QueryPairingProvider(provider.Addr.String(), "") + require.NoError(ts.T, err) + require.Equal(t, d, res1.StakeEntries[0].Description) + require.Equal(t, uint64(49), res1.StakeEntries[0].DelegateCommission) +} + +func TestMoveStake(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 1, 5, 0) + + delegator, _ := ts.AddAccount("del", 1, 1000000000) + provider0, _ := ts.GetAccount(common.PROVIDER, 0) + + // delegate and check delegatetotal + _, err := ts.TxDualstakingDelegate(delegator.Addr.String(), provider0.Addr.String(), types.NewInt64Coin(ts.TokenDenom(), 5000)) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(1000), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } + + _, err = ts.TxPairingMoveStake(provider0.GetVaultAddr(), SpecName(1), SpecName(0), testStake/2) + require.NoError(t, err) + + res, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(0)) + require.NoError(t, err) + require.Equal(t, int64(1500), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + + res, err = ts.QueryPairingProvider(provider0.Addr.String(), SpecName(1)) + require.NoError(t, err) + require.Equal(t, int64(500), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + + for i := 2; i < 5; i++ { + res, err := ts.QueryPairingProvider(provider0.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(1000), res.StakeEntries[0].DelegateTotal.Amount.Int64()) + } +} + +func TestPairingWithDelegationDistributions(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 0, 2, 1) + + stake := int64(1000) + delegator, _ := ts.AddAccount("del", 1, stake*10000) + client, _ := ts.GetAccount(common.CONSUMER, 0) + spec := ts.Spec(SpecName(0)) + + // stake for spec0 + for i := 0; i < 100; i++ { + acc, addr := ts.AddAccount(common.PROVIDER, i, testBalance) + err := ts.StakeProvider(acc.GetVaultAddr(), addr, spec, stake) + require.NoError(ts.T, err) + } + provider, _ := ts.GetAccount(common.PROVIDER, 0) + + // delegate a lot of stake to the first provider + _, err := ts.TxDualstakingDelegate(delegator.Addr.String(), provider.Addr.String(), types.NewInt64Coin(ts.TokenDenom(), stake*10000)) + require.NoError(t, err) + + numOfPairing := 0 + for i := 0; i < 100; i++ { + ts.AdvanceEpoch() + + // make sure the provider is in the pairing list + res, err := ts.QueryPairingGetPairing(spec.Index, client.Addr.String()) + require.NoError(t, err) + found := false + for _, p := range res.Providers { + if p.Address == provider.Addr.String() { + found = true + break + } + } + if found { + numOfPairing++ + } + } + require.Greater(t, numOfPairing, 95) + + spec = ts.Spec(SpecName(1)) + // stake to spec1 + for i := 0; i < 100; i++ { + acc, addr := ts.GetAccount(common.PROVIDER, i) + err := ts.StakeProvider(acc.GetVaultAddr(), addr, spec, stake) + require.NoError(ts.T, err) + } + + // check that provider is in pairing in spec1 + numOfPairing = 0 + for i := 0; i < 100; i++ { + ts.AdvanceEpoch() + + // make sure the provider is in the pairing list + res, err := ts.QueryPairingGetPairing(spec.Index, client.Addr.String()) + require.NoError(t, err) + found := false + for _, p := range res.Providers { + if p.Address == provider.Addr.String() { + found = true + break + } + } + if found { + numOfPairing++ + } + } + require.Greater(t, numOfPairing, 95) +} + +func TestDistributionCli(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 1, 100, 0) + + provider, _ := ts.GetAccount(common.PROVIDER, 0) + + res, err := ts.QueryPairingProvider(provider.Addr.String(), "") + require.NoError(t, err) + + // distribute each provider 0.1% and last with 90.1% + distribution := "" + for i := 0; i < 99; i++ { + distribution = distribution + SpecName(i) + "," + "0.1" + "," + } + distribution = distribution + SpecName(99) + "," + "90.1" + + // get the msgs and run + msgs, err := cli.CalculateDistbiruitions(provider.Addr.String(), res.StakeEntries, distribution) + require.NoError(t, err) + for _, msgRaw := range msgs { + msg, ok := msgRaw.(*pairingtypes.MsgMoveProviderStake) + require.True(t, ok) + require.Equal(t, provider.Addr.String(), msg.Creator) + require.Equal(t, SpecName(99), msg.DstChain) + require.NotEqual(t, SpecName(99), msg.SrcChain) + require.Equal(t, int64(90000), msg.Amount.Amount.Int64()) + + _, err := ts.Servers.PairingServer.MoveProviderStake(ts.Ctx, msg) + require.NoError(t, err) + } + + // check the stake entries on chain + for i := 0; i < 99; i++ { + res, err = ts.QueryPairingProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, int64(10000), res.StakeEntries[0].Stake.Amount.Int64()) + } + + res, err = ts.QueryPairingProvider(provider.Addr.String(), SpecName(99)) + require.NoError(t, err) + require.Equal(t, int64(9010000), res.StakeEntries[0].Stake.Amount.Int64()) + + // distribute back to 1% each + res, err = ts.QueryPairingProvider(provider.Addr.String(), "") + require.NoError(t, err) + + distribution = "" + for i := 0; i < 100; i++ { + distribution = distribution + SpecName(i) + "," + "1" + "," + } + distribution = distribution[:len(distribution)-1] + + msgs, err = cli.CalculateDistbiruitions(provider.Addr.String(), res.StakeEntries, distribution) + require.NoError(t, err) + for _, msgRaw := range msgs { + msg, ok := msgRaw.(*pairingtypes.MsgMoveProviderStake) + require.True(t, ok) + require.Equal(t, provider.Addr.String(), msg.Creator) + require.Equal(t, SpecName(99), msg.SrcChain) + require.NotEqual(t, SpecName(99), msg.DstChain) + require.Equal(t, int64(90000), msg.Amount.Amount.Int64()) + + _, err := ts.Servers.PairingServer.MoveProviderStake(ts.Ctx, msg) + require.NoError(t, err) + } + + for i := 0; i < 100; i++ { + res, err = ts.QueryPairingProvider(provider.Addr.String(), SpecName(i)) + require.NoError(t, err) + require.Equal(t, testStake, res.StakeEntries[0].Stake.Amount.Int64()) + } +} diff --git a/x/pairing/keeper/staking.go b/x/pairing/keeper/staking.go index 5f4d179e7c..a2e0850e03 100644 --- a/x/pairing/keeper/staking.go +++ b/x/pairing/keeper/staking.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/lavanet/lava/v3/utils" + "github.com/lavanet/lava/v3/utils/lavaslices" epochstoragetypes "github.com/lavanet/lava/v3/x/epochstorage/types" "github.com/lavanet/lava/v3/x/pairing/types" planstypes "github.com/lavanet/lava/v3/x/plans/types" @@ -23,6 +24,19 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin logger := k.Logger(ctx) specChainID := chainID + metadata, err := k.epochStorageKeeper.GetMetadata(ctx, provider) + if err != nil { + // first provider with this address + metadata = epochstoragetypes.ProviderMetadata{ + Provider: provider, + Vault: creator, + Chains: []string{chainID}, + TotalDelegations: sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), sdk.ZeroInt()), + } + } else { + metadata.Chains = lavaslices.AddUnique(metadata.Chains, chainID) + } + spec, err := k.specKeeper.GetExpandedSpec(ctx, specChainID) if err != nil || !spec.Enabled { return utils.LavaFormatWarning("spec not found or not active", err, @@ -31,14 +45,6 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin } // if we get here, the spec is active and supported - if amount.IsLT(k.dualstakingKeeper.MinSelfDelegation(ctx)) { // we count on this to also check the denom - return utils.LavaFormatWarning("insufficient stake amount", fmt.Errorf("stake amount smaller than MinSelfDelegation"), - utils.Attribute{Key: "spec", Value: specChainID}, - utils.Attribute{Key: "provider", Value: creator}, - utils.Attribute{Key: "stake", Value: amount}, - utils.Attribute{Key: "minSelfDelegation", Value: k.dualstakingKeeper.MinSelfDelegation(ctx).String()}, - ) - } senderAddr, err := sdk.AccAddressFromBech32(creator) if err != nil { return utils.LavaFormatWarning("invalid address", err, @@ -94,15 +100,14 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin // verify that the provider only tries to change non-stake related traits of the stake entry // a provider can be the same as the vault, so we verify it's not the case if isProvider { - if delegationCommission != existingEntry.DelegateCommission || + if delegationCommission != metadata.DelegateCommission || !amount.Equal(existingEntry.Stake) { return utils.LavaFormatWarning("only vault address can change stake/delegation related properties of the stake entry", fmt.Errorf("invalid modification request for stake entry"), utils.LogAttr("creator", creator), utils.LogAttr("vault", existingEntry.Vault), utils.LogAttr("provider", provider), utils.LogAttr("description", description.String()), - utils.LogAttr("req_delegation_limit", delegationLimit), - utils.LogAttr("current_delegation_commission", existingEntry.DelegateCommission), + utils.LogAttr("current_delegation_commission", metadata.DelegateCommission), utils.LogAttr("req_delegation_commission", delegationCommission), utils.LogAttr("current_stake", existingEntry.Stake.String()), utils.LogAttr("req_stake", amount.String()), @@ -131,48 +136,52 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin // if the provider has no delegations then we dont limit the changes if !existingEntry.DelegateTotal.IsZero() { // if there was a change in the last 24h than we dont allow changes - if ctx.BlockTime().UTC().Unix()-int64(existingEntry.LastChange) < int64(CHANGE_WINDOW.Seconds()) { - if delegationCommission != existingEntry.DelegateCommission { + if ctx.BlockTime().UTC().Unix()-int64(metadata.LastChange) < int64(CHANGE_WINDOW.Seconds()) { + if delegationCommission != metadata.DelegateCommission { return utils.LavaFormatWarning(fmt.Sprintf("stake entry commmision or delegate limit can only be changes once in %s", CHANGE_WINDOW), nil, - utils.LogAttr("last_change_time", existingEntry.LastChange)) + utils.LogAttr("last_change_time", metadata.LastChange)) } } // check that the change is not mode than MAX_CHANGE_RATE - if int64(delegationCommission)-int64(existingEntry.DelegateCommission) > MAX_CHANGE_RATE { + if int64(delegationCommission)-int64(metadata.DelegateCommission) > MAX_CHANGE_RATE { return utils.LavaFormatWarning("stake entry commission increase too high", fmt.Errorf("commission change cannot increase by more than %d at a time", MAX_CHANGE_RATE), - utils.LogAttr("original_commission", existingEntry.DelegateCommission), + utils.LogAttr("original_commission", metadata.DelegateCommission), utils.LogAttr("wanted_commission", delegationCommission), ) } } // we dont change stakeAppliedBlocks and chain once they are set, if they need to change, unstake first + beforeAmount := existingEntry.Stake + increase := amount.Amount.GT(existingEntry.Stake.Amount) + decrease := amount.Amount.LT(existingEntry.Stake.Amount) existingEntry.Geolocation = geolocation existingEntry.Endpoints = endpointsVerified - existingEntry.Description = description - existingEntry.DelegateCommission = delegationCommission - existingEntry.LastChange = uint64(ctx.BlockTime().UTC().Unix()) + metadata.Description = description + metadata.DelegateCommission = delegationCommission + metadata.LastChange = uint64(ctx.BlockTime().UTC().Unix()) + existingEntry.Stake = amount k.epochStorageKeeper.SetStakeEntryCurrent(ctx, existingEntry) - - if amount.Amount.GT(existingEntry.Stake.Amount) { + k.epochStorageKeeper.SetMetadata(ctx, metadata) + if increase { // delegate the difference - diffAmount := amount.Sub(existingEntry.Stake) - err = k.dualstakingKeeper.DelegateFull(ctx, existingEntry.Vault, validator, existingEntry.Address, chainID, diffAmount) + diffAmount := amount.Sub(beforeAmount) + err = k.dualstakingKeeper.DelegateFull(ctx, existingEntry.Vault, validator, existingEntry.Address, diffAmount, true) if err != nil { details = append(details, utils.Attribute{Key: "neededStake", Value: amount.Sub(existingEntry.Stake).String()}) - return utils.LavaFormatWarning("insufficient funds to pay for difference in stake", err, + return utils.LavaFormatWarning("failed to increase stake", err, details..., ) } - } else if amount.Amount.LT(existingEntry.Stake.Amount) { + } else if decrease { // unbond the difference - diffAmount := existingEntry.Stake.Sub(amount) - err = k.dualstakingKeeper.UnbondFull(ctx, existingEntry.Vault, validator, existingEntry.Address, chainID, diffAmount, false) + diffAmount := beforeAmount.Sub(amount) + err = k.dualstakingKeeper.UnbondFull(ctx, existingEntry.Vault, validator, existingEntry.Address, diffAmount, true) if err != nil { details = append(details, utils.Attribute{Key: "neededStake", Value: amount.Sub(existingEntry.Stake).String()}) - return utils.LavaFormatWarning("insufficient funds to pay for difference in stake", err, + return utils.LavaFormatWarning("failed to decrease stake", err, details..., ) } @@ -201,6 +210,13 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin } } + if creator != metadata.Vault { + return utils.LavaFormatWarning("creator does not match the provider vault", err, + utils.Attribute{Key: "vault", Value: metadata.Vault}, + utils.Attribute{Key: "creator", Value: creator}, + ) + } + // entry isn't staked so add him details := []utils.Attribute{ {Key: "spec", Value: specChainID}, @@ -220,39 +236,49 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin ) } - delegations, err := k.dualstakingKeeper.GetProviderDelegators(ctx, provider, nextEpoch) - if err != nil { - utils.LavaFormatWarning("cannot get provider's delegators", err, - utils.LogAttr("provider", provider), - utils.LogAttr("block", nextEpoch), - ) + stakeAmount := amount + // creating a new provider, fetch old delegation + if len(metadata.Chains) == 1 { + delegations, err := k.dualstakingKeeper.GetProviderDelegators(ctx, provider) + if err == nil { + for _, d := range delegations { + if d.Delegator == creator { + stakeAmount = stakeAmount.Add(d.Amount) + } else { + metadata.TotalDelegations = metadata.TotalDelegations.Add(d.Amount) + } + } + } } - for _, d := range delegations { - if (d.Delegator == creator && d.Provider == provider) || d.ChainID != chainID { - // ignore provider self delegation (delegator = vault, provider = provider) or delegations from other chains - continue - } - delegateTotal = delegateTotal.Add(d.Amount.Amount) + if stakeAmount.IsLT(k.dualstakingKeeper.MinSelfDelegation(ctx)) { // we count on this to also check the denom + return utils.LavaFormatWarning("insufficient stake amount", fmt.Errorf("stake amount smaller than MinSelfDelegation"), + utils.Attribute{Key: "spec", Value: specChainID}, + utils.Attribute{Key: "provider", Value: creator}, + utils.Attribute{Key: "stake", Value: amount}, + utils.Attribute{Key: "minSelfDelegation", Value: k.dualstakingKeeper.MinSelfDelegation(ctx).String()}, + ) } stakeEntry := epochstoragetypes.StakeEntry{ - Stake: sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), sdk.ZeroInt()), // we set this to 0 since the delegate will take care of this - Address: provider, - StakeAppliedBlock: stakeAppliedBlock, - Endpoints: endpointsVerified, - Geolocation: geolocation, - Chain: chainID, - Description: description, - DelegateTotal: sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), delegateTotal), - DelegateCommission: delegationCommission, - Vault: creator, // the stake-provider TX creator is always regarded as the vault address - LastChange: uint64(ctx.BlockTime().UTC().Unix()), + Stake: stakeAmount, + Address: provider, + StakeAppliedBlock: stakeAppliedBlock, + Endpoints: endpointsVerified, + Geolocation: geolocation, + Chain: chainID, + DelegateTotal: sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), delegateTotal), + Vault: creator, // the stake-provider TX creator is always regarded as the vault address } + metadata.DelegateCommission = delegationCommission + metadata.LastChange = uint64(ctx.BlockTime().UTC().Unix()) + metadata.Description = description + + k.epochStorageKeeper.SetMetadata(ctx, metadata) k.epochStorageKeeper.SetStakeEntryCurrent(ctx, stakeEntry) - err = k.dualstakingKeeper.DelegateFull(ctx, stakeEntry.Vault, validator, stakeEntry.Address, chainID, amount) + err = k.dualstakingKeeper.DelegateFull(ctx, stakeEntry.Vault, validator, stakeEntry.Address, amount, true) if err != nil { return utils.LavaFormatWarning("provider self delegation failed", err, details..., diff --git a/x/pairing/keeper/unresponsive_provider_test.go b/x/pairing/keeper/unresponsive_provider_test.go index abaf11842c..28f34f1370 100644 --- a/x/pairing/keeper/unresponsive_provider_test.go +++ b/x/pairing/keeper/unresponsive_provider_test.go @@ -429,7 +429,7 @@ func TestJailProviderForUnresponsiveness(t *testing.T) { // jail first time jailProvider() // try to unfreeze with increase of self delegation - _, err = ts.TxDualstakingDelegate(provider1, provider1, ts.spec.Index, common.NewCoin(ts.TokenDenom(), 1)) + _, err = ts.TxDualstakingDelegate(provider1, provider1, common.NewCoin(ts.TokenDenom(), 1)) require.Nil(t, err) _, err = ts.TxPairingUnfreezeProvider(provider1, ts.spec.Index) @@ -457,7 +457,7 @@ func TestJailProviderForUnresponsiveness(t *testing.T) { // jail third time jailProvider() // try to unfreeze with increase of self delegation - _, err = ts.TxDualstakingDelegate(provider1, provider1, ts.spec.Index, common.NewCoin(ts.TokenDenom(), 1)) + _, err = ts.TxDualstakingDelegate(provider1, provider1, common.NewCoin(ts.TokenDenom(), 1)) require.Nil(t, err) _, err = ts.TxPairingUnfreezeProvider(provider1, ts.spec.Index) diff --git a/x/pairing/keeper/unstaking.go b/x/pairing/keeper/unstaking.go index ff4f6eafb0..c86bb75a48 100644 --- a/x/pairing/keeper/unstaking.go +++ b/x/pairing/keeper/unstaking.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/v3/utils" + epochstoragetypes "github.com/lavanet/lava/v3/x/epochstorage/types" "github.com/lavanet/lava/v3/x/pairing/types" ) @@ -36,8 +37,8 @@ func (k Keeper) UnstakeEntry(ctx sdk.Context, validator, chainID, creator, unsta ) } - if creator != existingEntry.Vault { - return utils.LavaFormatWarning("can't unstake entry with provider address, only vault address is allowed to unstake", fmt.Errorf("provider unstake failed"), + if creator != existingEntry.Vault && creator != existingEntry.Address { + return utils.LavaFormatWarning("unstake can be don only by provider or vault", fmt.Errorf("provider unstake failed"), utils.LogAttr("creator", creator), utils.LogAttr("provider", existingEntry.Address), utils.LogAttr("vault", existingEntry.Vault), @@ -45,13 +46,46 @@ func (k Keeper) UnstakeEntry(ctx sdk.Context, validator, chainID, creator, unsta ) } - // the stake entry is removed inside UnbondFull - err := k.dualstakingKeeper.UnbondFull(ctx, existingEntry.Vault, validator, existingEntry.Address, existingEntry.GetChain(), existingEntry.Stake, true) - if err != nil { - return utils.LavaFormatWarning("can't unbond self delegation", err, - utils.Attribute{Key: "address", Value: existingEntry.Address}, - utils.Attribute{Key: "spec", Value: chainID}, - ) + amount := existingEntry.Stake + k.epochStorageKeeper.RemoveStakeEntryCurrent(ctx, existingEntry.Chain, existingEntry.Address) + + if existingEntry.Vault == creator { + // remove delegation + err := k.dualstakingKeeper.UnbondFull(ctx, existingEntry.Vault, validator, existingEntry.Address, amount, true) + if err != nil { + return utils.LavaFormatWarning("can't unbond self delegation", err, + utils.Attribute{Key: "address", Value: existingEntry.Address}, + utils.Attribute{Key: "spec", Value: chainID}, + ) + } + } else { + // provider is not vault so delegation stays. + // distribute stake between other chains + metadata, err := k.epochStorageKeeper.GetMetadata(ctx, existingEntry.Address) + if err == nil { + entries := []*epochstoragetypes.StakeEntry{} + for _, chain := range metadata.Chains { + entry, found := k.epochStorageKeeper.GetStakeEntryCurrent(ctx, chain, existingEntry.Address) + if found { + entries = append(entries, &entry) + } else { + utils.LavaFormatError("did not find stake entry that exists in metadata", nil, + utils.LogAttr("provider", existingEntry.Address), + utils.LogAttr("chain", chain), + ) + } + } + + total := amount.Amount + count := int64(len(entries)) + for _, entry := range entries { + part := total.QuoRaw(count) + entry.Stake = entry.Stake.AddAmount(part) + total = total.Sub(part) + count-- + k.epochStorageKeeper.SetStakeEntryCurrent(ctx, *entry) + } + } } details := map[string]string{ @@ -68,13 +102,6 @@ func (k Keeper) UnstakeEntry(ctx sdk.Context, validator, chainID, creator, unsta } func (k Keeper) UnstakeEntryForce(ctx sdk.Context, chainID, provider, unstakeDescription string) error { - providerAddr, err := sdk.AccAddressFromBech32(provider) - if err != nil { - return utils.LavaFormatWarning("invalid address", err, - utils.Attribute{Key: "provider", Value: provider}, - ) - } - existingEntry, entryExists := k.epochStorageKeeper.GetStakeEntryCurrent(ctx, chainID, provider) if !entryExists { return utils.LavaFormatWarning("can't unstake Entry, stake entry not found for address", fmt.Errorf("stake entry not found"), @@ -83,7 +110,15 @@ func (k Keeper) UnstakeEntryForce(ctx sdk.Context, chainID, provider, unstakeDes ) } totalAmount := existingEntry.Stake.Amount - delegations := k.stakingKeeper.GetAllDelegatorDelegations(ctx, providerAddr) + vaultAcc, err := sdk.AccAddressFromBech32(existingEntry.Vault) + if err != nil { + return utils.LavaFormatError("can't unstake entry, invalid vault address", err, + utils.LogAttr("provider", provider), + utils.LogAttr("chain", chainID), + utils.LogAttr("vault", existingEntry.Vault), + ) + } + delegations := k.stakingKeeper.GetAllDelegatorDelegations(ctx, vaultAcc) for _, delegation := range delegations { validator, found := k.stakingKeeper.GetValidator(ctx, delegation.GetValidatorAddr()) @@ -95,7 +130,7 @@ func (k Keeper) UnstakeEntryForce(ctx sdk.Context, chainID, provider, unstakeDes amount = totalAmount } totalAmount = totalAmount.Sub(amount) - err = k.dualstakingKeeper.UnbondFull(ctx, existingEntry.Vault, validator.OperatorAddress, existingEntry.Address, existingEntry.GetChain(), sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), amount), true) + err = k.dualstakingKeeper.UnbondFull(ctx, existingEntry.Vault, validator.OperatorAddress, existingEntry.Address, sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), amount), true) if err != nil { return utils.LavaFormatWarning("can't unbond self delegation", err, utils.LogAttr("provider", existingEntry.Address), @@ -105,9 +140,6 @@ func (k Keeper) UnstakeEntryForce(ctx sdk.Context, chainID, provider, unstakeDes } if totalAmount.IsZero() { - existingEntry, _ := k.epochStorageKeeper.GetStakeEntryCurrent(ctx, chainID, provider) - k.epochStorageKeeper.RemoveStakeEntryCurrent(ctx, chainID, existingEntry.Address) - details := map[string]string{ "address": existingEntry.GetAddress(), "chainID": existingEntry.GetChain(), diff --git a/x/pairing/keeper/unstaking_test.go b/x/pairing/keeper/unstaking_test.go index cff2bf2537..7e0b3ebb5c 100644 --- a/x/pairing/keeper/unstaking_test.go +++ b/x/pairing/keeper/unstaking_test.go @@ -24,7 +24,7 @@ func TestUnstakeAndSlashProposal(t *testing.T) { for i := 0; i < delegators; i++ { _, delegator := ts.GetAccount(common.CONSUMER, i) beforeSlashDelegation[delegator] = sdk.NewInt(1000 * int64(i+1)) - _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.BondDenom(), beforeSlashDelegation[delegator])) + _, err := ts.TxDualstakingDelegate(delegator, provider, sdk.NewCoin(ts.BondDenom(), beforeSlashDelegation[delegator])) require.NoError(t, err) delegatorsSlashing = append(delegatorsSlashing, types.DelegatorSlashing{Delegator: delegator, SlashingAmount: sdk.NewCoin(ts.BondDenom(), sdk.NewInt(1000/3*int64(i+1)))}) } @@ -38,7 +38,7 @@ func TestUnstakeAndSlashProposal(t *testing.T) { ) require.NoError(t, err) - result, err := ts.QueryDualstakingProviderDelegators(provider, true) + result, err := ts.QueryDualstakingProviderDelegators(provider) require.NoError(t, err) for _, d := range result.Delegations { for _, s := range delegatorsSlashing { diff --git a/x/pairing/module.go b/x/pairing/module.go index 5f605c9eb7..8692ed8ade 100644 --- a/x/pairing/module.go +++ b/x/pairing/module.go @@ -133,9 +133,13 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(types.ModuleName, 2, migrator.MigrateVersion2To3); err != nil { panic(fmt.Errorf("%s: failed to register migration to v3: %w", types.ModuleName, err)) } - // register v3 -> v4 migration - if err := cfg.RegisterMigration(types.ModuleName, 3, migrator.MigrateVersion3To4); err != nil { - panic(fmt.Errorf("%s: failed to register migration to v4: %w", types.ModuleName, err)) + // // register v3 -> v4 migration + // if err := cfg.RegisterMigration(types.ModuleName, 3, migrator.MigrateVersion3To4); err != nil { + // panic(fmt.Errorf("%s: failed to register migration to v4: %w", types.ModuleName, err)) + // } + // register v4 -> v5 migration + if err := cfg.RegisterMigration(types.ModuleName, 4, migrator.MigrateVersion4To5); err != nil { + panic(fmt.Errorf("%s: failed to register migration to v5: %w", types.ModuleName, err)) } } @@ -161,7 +165,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 4 } +func (AppModule) ConsensusVersion() uint64 { return 5 } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { diff --git a/x/pairing/types/codec.go b/x/pairing/types/codec.go index abac0115f6..8c75476c0d 100644 --- a/x/pairing/types/codec.go +++ b/x/pairing/types/codec.go @@ -16,6 +16,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgRelayPayment{}, "pairing/RelayPayment", nil) cdc.RegisterConcrete(&MsgFreezeProvider{}, "pairing/Freeze", nil) cdc.RegisterConcrete(&MsgUnfreezeProvider{}, "pairing/Unfreeze", nil) + cdc.RegisterConcrete(&MsgMoveProviderStake{}, "pairing/MoveProviderStake", nil) // this line is used by starport scaffolding # 2 } @@ -35,6 +36,9 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgUnfreezeProvider{}, ) + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgMoveProviderStake{}, + ) // this line is used by starport scaffolding # 3 msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) diff --git a/x/pairing/types/expected_keepers.go b/x/pairing/types/expected_keepers.go index 6a8fb13978..815b33048e 100644 --- a/x/pairing/types/expected_keepers.go +++ b/x/pairing/types/expected_keepers.go @@ -53,6 +53,8 @@ type EpochstorageKeeper interface { GetAllStakeEntriesCurrent(ctx sdk.Context) []epochstoragetypes.StakeEntry RemoveStakeEntryCurrent(ctx sdk.Context, chainID string, provider string) GetEpochHash(ctx sdk.Context, epoch uint64) []byte + GetMetadata(ctx sdk.Context, provider string) (epochstoragetypes.ProviderMetadata, error) + SetMetadata(ctx sdk.Context, metadata epochstoragetypes.ProviderMetadata) } type AccountKeeper interface { @@ -102,11 +104,13 @@ type DowntimeKeeper interface { } type DualstakingKeeper interface { - RewardProvidersAndDelegators(ctx sdk.Context, providerAddr string, chainID string, totalReward sdk.Coins, senderModule string, calcOnlyProvider bool, calcOnlyDelegators bool, calcOnlyContributor bool) (providerReward sdk.Coins, totalRewards sdk.Coins, err error) - DelegateFull(ctx sdk.Context, delegator string, validator string, provider string, chainID string, amount sdk.Coin) error - UnbondFull(ctx sdk.Context, delegator string, validator string, provider string, chainID string, amount sdk.Coin, unstake bool) error - GetProviderDelegators(ctx sdk.Context, provider string, epoch uint64) ([]dualstakingtypes.Delegation, error) + RewardProvidersAndDelegators(ctx sdk.Context, providerAddr string, chainID string, totalReward sdk.Coins, senderModule string, calcOnlyProvider bool, calcOnlyDelegators bool, calcOnlyContributor bool) (providerReward sdk.Coins, err error) + DelegateFull(ctx sdk.Context, delegator string, validator string, provider string, amount sdk.Coin, stake bool) error + UnbondFull(ctx sdk.Context, delegator string, validator string, provider string, amount sdk.Coin, stake bool) error + GetProviderDelegators(ctx sdk.Context, provider string) ([]dualstakingtypes.Delegation, error) MinSelfDelegation(ctx sdk.Context) sdk.Coin + GetDelegation(ctx sdk.Context, provider, delegator string) (dualstakingtypes.Delegation, bool) + AfterDelegationModified(ctx sdk.Context, delegator, provider string, amount sdk.Coin, increase, stake bool) (err error) } type FixationStoreKeeper interface { diff --git a/x/pairing/types/message_move_provider_stake.go b/x/pairing/types/message_move_provider_stake.go new file mode 100644 index 0000000000..442085c265 --- /dev/null +++ b/x/pairing/types/message_move_provider_stake.go @@ -0,0 +1,49 @@ +package types + +import ( + sdkerrors "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + legacyerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const TypeMsgMoveProviderStake = "MoveProviderStake" + +var _ sdk.Msg = &MsgMoveProviderStake{} + +func NewMsgMoveProviderStake(creator string, srcChain, dstChain string, amount sdk.Coin) *MsgMoveProviderStake { + return &MsgMoveProviderStake{ + Creator: creator, + SrcChain: srcChain, + DstChain: dstChain, + Amount: amount, + } +} + +func (msg *MsgMoveProviderStake) Route() string { + return RouterKey +} + +func (msg *MsgMoveProviderStake) Type() string { + return TypeMsgMoveProviderStake +} + +func (msg *MsgMoveProviderStake) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgMoveProviderStake) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgMoveProviderStake) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return sdkerrors.Wrapf(legacyerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + return nil +} diff --git a/x/pairing/types/tx.pb.go b/x/pairing/types/tx.pb.go index 0033fa0454..59b17da6ba 100644 --- a/x/pairing/types/tx.pb.go +++ b/x/pairing/types/tx.pb.go @@ -628,6 +628,110 @@ func (m *MsgUnfreezeProviderResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUnfreezeProviderResponse proto.InternalMessageInfo +type MsgMoveProviderStake struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + SrcChain string `protobuf:"bytes,2,opt,name=src_chain,json=srcChain,proto3" json:"src_chain,omitempty"` + DstChain string `protobuf:"bytes,3,opt,name=dst_chain,json=dstChain,proto3" json:"dst_chain,omitempty"` + Amount types.Coin `protobuf:"bytes,4,opt,name=amount,proto3" json:"amount"` +} + +func (m *MsgMoveProviderStake) Reset() { *m = MsgMoveProviderStake{} } +func (m *MsgMoveProviderStake) String() string { return proto.CompactTextString(m) } +func (*MsgMoveProviderStake) ProtoMessage() {} +func (*MsgMoveProviderStake) Descriptor() ([]byte, []int) { + return fileDescriptor_07b85a84d2198a91, []int{11} +} +func (m *MsgMoveProviderStake) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMoveProviderStake) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMoveProviderStake.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 *MsgMoveProviderStake) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMoveProviderStake.Merge(m, src) +} +func (m *MsgMoveProviderStake) XXX_Size() int { + return m.Size() +} +func (m *MsgMoveProviderStake) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMoveProviderStake.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMoveProviderStake proto.InternalMessageInfo + +func (m *MsgMoveProviderStake) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgMoveProviderStake) GetSrcChain() string { + if m != nil { + return m.SrcChain + } + return "" +} + +func (m *MsgMoveProviderStake) GetDstChain() string { + if m != nil { + return m.DstChain + } + return "" +} + +func (m *MsgMoveProviderStake) GetAmount() types.Coin { + if m != nil { + return m.Amount + } + return types.Coin{} +} + +type MsgMoveProviderStakeResponse struct { +} + +func (m *MsgMoveProviderStakeResponse) Reset() { *m = MsgMoveProviderStakeResponse{} } +func (m *MsgMoveProviderStakeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMoveProviderStakeResponse) ProtoMessage() {} +func (*MsgMoveProviderStakeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_07b85a84d2198a91, []int{12} +} +func (m *MsgMoveProviderStakeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMoveProviderStakeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMoveProviderStakeResponse.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 *MsgMoveProviderStakeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMoveProviderStakeResponse.Merge(m, src) +} +func (m *MsgMoveProviderStakeResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMoveProviderStakeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMoveProviderStakeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMoveProviderStakeResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgStakeProvider)(nil), "lavanet.lava.pairing.MsgStakeProvider") proto.RegisterType((*MsgStakeProviderResponse)(nil), "lavanet.lava.pairing.MsgStakeProviderResponse") @@ -640,68 +744,75 @@ func init() { proto.RegisterType((*MsgFreezeProviderResponse)(nil), "lavanet.lava.pairing.MsgFreezeProviderResponse") proto.RegisterType((*MsgUnfreezeProvider)(nil), "lavanet.lava.pairing.MsgUnfreezeProvider") proto.RegisterType((*MsgUnfreezeProviderResponse)(nil), "lavanet.lava.pairing.MsgUnfreezeProviderResponse") + proto.RegisterType((*MsgMoveProviderStake)(nil), "lavanet.lava.pairing.MsgMoveProviderStake") + proto.RegisterType((*MsgMoveProviderStakeResponse)(nil), "lavanet.lava.pairing.MsgMoveProviderStakeResponse") } func init() { proto.RegisterFile("lavanet/lava/pairing/tx.proto", fileDescriptor_07b85a84d2198a91) } var fileDescriptor_07b85a84d2198a91 = []byte{ - // 887 bytes of a gzipped FileDescriptorProto + // 969 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x41, 0x73, 0xdb, 0x44, - 0x14, 0x8e, 0x12, 0xc5, 0xb1, 0x9f, 0xdb, 0xc4, 0xd9, 0xa4, 0x44, 0x51, 0x53, 0x63, 0x54, 0x20, - 0x26, 0x03, 0x12, 0x49, 0x0f, 0xcc, 0xf8, 0xe8, 0x86, 0x32, 0xed, 0xd4, 0x43, 0x46, 0x19, 0x0e, - 0x70, 0xf1, 0xac, 0xa5, 0x45, 0x51, 0x63, 0x69, 0x35, 0xda, 0xad, 0xa7, 0xe1, 0xcc, 0x89, 0x0b, - 0xf0, 0x13, 0xb8, 0x71, 0xec, 0xcf, 0xe8, 0xb1, 0x47, 0x4e, 0x0c, 0x93, 0x1c, 0xfa, 0x37, 0x98, - 0x5d, 0xad, 0x64, 0x4b, 0x76, 0x3a, 0x9e, 0x81, 0x4b, 0xac, 0xb7, 0xef, 0x7b, 0xef, 0xfb, 0xf4, - 0xbd, 0xb7, 0x19, 0xc1, 0x83, 0x31, 0x9e, 0xe0, 0x98, 0x70, 0x47, 0xfc, 0x3a, 0x09, 0x0e, 0xd3, - 0x30, 0x0e, 0x1c, 0xfe, 0xca, 0x4e, 0x52, 0xca, 0x29, 0xda, 0x55, 0x69, 0x5b, 0xfc, 0xda, 0x2a, - 0x6d, 0xb6, 0x3d, 0xca, 0x22, 0xca, 0x9c, 0x11, 0x66, 0xc4, 0x99, 0x1c, 0x8f, 0x08, 0xc7, 0xc7, - 0x8e, 0x47, 0xc3, 0x38, 0xab, 0x32, 0x77, 0x03, 0x1a, 0x50, 0xf9, 0xe8, 0x88, 0x27, 0x75, 0xda, - 0x2d, 0x51, 0x91, 0x84, 0x7a, 0x17, 0x8c, 0xd3, 0x14, 0x07, 0xc4, 0x21, 0xb1, 0x9f, 0xd0, 0x30, - 0xe6, 0x0a, 0xd9, 0x59, 0x28, 0x2a, 0x25, 0x63, 0x7c, 0xa5, 0x10, 0x1f, 0x2b, 0x05, 0x8c, 0xe3, - 0x4b, 0x91, 0xcb, 0x45, 0xa8, 0x58, 0xa1, 0xb6, 0x71, 0x14, 0xc6, 0xd4, 0x91, 0x7f, 0xb3, 0x23, - 0xeb, 0x57, 0x1d, 0x5a, 0x03, 0x16, 0x9c, 0x73, 0x7c, 0x49, 0xce, 0x52, 0x3a, 0x09, 0x7d, 0x92, - 0x22, 0x03, 0x36, 0xbc, 0x94, 0x60, 0x4e, 0x53, 0x43, 0xeb, 0x68, 0xdd, 0x86, 0x9b, 0x87, 0x32, - 0x73, 0x81, 0xc3, 0xf8, 0xe9, 0xa9, 0xb1, 0xaa, 0x32, 0x59, 0x88, 0xbe, 0x82, 0x1a, 0x8e, 0xe8, - 0xcb, 0x98, 0x1b, 0x6b, 0x1d, 0xad, 0xdb, 0x3c, 0xd9, 0xb7, 0x33, 0x49, 0xb6, 0x30, 0xc5, 0x56, - 0x7a, 0xec, 0xc7, 0x34, 0x8c, 0xfb, 0xfa, 0x9b, 0xbf, 0x3f, 0x5c, 0x71, 0x15, 0x1c, 0x7d, 0x03, - 0x8d, 0xfc, 0x75, 0x99, 0xa1, 0x77, 0xd6, 0xba, 0xcd, 0x93, 0x87, 0x76, 0xc9, 0xe6, 0x59, 0x6b, - 0xec, 0xaf, 0x15, 0x56, 0x75, 0x99, 0xd6, 0xa2, 0x0e, 0x34, 0x03, 0x42, 0xc7, 0xd4, 0xc3, 0x3c, - 0xa4, 0xb1, 0xb1, 0xde, 0xd1, 0xba, 0xeb, 0xee, 0xec, 0x11, 0x7a, 0x02, 0x9b, 0x3e, 0x19, 0x93, - 0x00, 0x73, 0x32, 0x1c, 0x87, 0x51, 0xc8, 0x8d, 0x8d, 0xe5, 0xb4, 0xde, 0xcd, 0xcb, 0x9e, 0x8b, - 0x2a, 0xe4, 0xc0, 0x4e, 0xd1, 0xc7, 0xa3, 0x51, 0x14, 0x32, 0x26, 0x18, 0xeb, 0x1d, 0xad, 0xab, - 0xbb, 0x28, 0x4f, 0x3d, 0x2e, 0x32, 0xe8, 0x00, 0x1a, 0x13, 0x3c, 0x0e, 0x7d, 0x69, 0x69, 0x43, - 0x1a, 0x37, 0x3d, 0x10, 0xa6, 0x62, 0xdf, 0x4f, 0x09, 0x63, 0x06, 0x64, 0xa6, 0xaa, 0x10, 0x9d, - 0x41, 0xd3, 0x27, 0xcc, 0x4b, 0xc3, 0x44, 0xbe, 0x52, 0x53, 0xaa, 0x7d, 0x98, 0xab, 0xcd, 0x87, - 0x9b, 0x0b, 0x3e, 0x9d, 0x42, 0xfb, 0x0d, 0xa1, 0xfb, 0xcf, 0x77, 0xaf, 0x8f, 0x34, 0x77, 0xb6, - 0x45, 0xcf, 0xfc, 0xe5, 0xdd, 0xeb, 0xa3, 0x7b, 0xf9, 0x0a, 0x95, 0xc6, 0xfe, 0x4c, 0xaf, 0xd7, - 0x5a, 0x1b, 0x96, 0x09, 0x46, 0x75, 0x21, 0x5c, 0xc2, 0x12, 0x1a, 0x33, 0x62, 0xfd, 0xac, 0x01, - 0x1a, 0xb0, 0xe0, 0xbb, 0x98, 0xfd, 0xe7, 0x7d, 0x29, 0x59, 0xb2, 0x56, 0xb1, 0xa4, 0x77, 0x20, - 0x64, 0xee, 0xe5, 0x32, 0x2b, 0x7c, 0xd6, 0x01, 0x98, 0xf3, 0x2a, 0x0a, 0x91, 0xbf, 0xaf, 0xc2, - 0xd6, 0x80, 0x05, 0xae, 0xb8, 0x1e, 0x67, 0xf8, 0x2a, 0x22, 0x31, 0x7f, 0x8f, 0xc2, 0x1e, 0xd4, - 0xe4, 0x45, 0x62, 0xc6, 0xaa, 0xdc, 0x3d, 0xcb, 0x5e, 0x74, 0xc5, 0x6d, 0xd9, 0xed, 0x9c, 0xc8, - 0x71, 0xba, 0xaa, 0x02, 0x7d, 0x0e, 0xdb, 0x33, 0xde, 0x9e, 0x73, 0x81, 0x34, 0x74, 0xd9, 0x7f, - 0x3e, 0x81, 0xbe, 0x87, 0xdd, 0x31, 0xe6, 0x84, 0xf1, 0xe1, 0x68, 0x4c, 0xbd, 0xcb, 0x61, 0x4a, - 0x12, 0x9a, 0x72, 0x66, 0xac, 0x4b, 0xde, 0xc3, 0xc5, 0xbc, 0xcf, 0x65, 0x45, 0x5f, 0x14, 0xb8, - 0x12, 0xef, 0xa2, 0x71, 0xf5, 0x88, 0xf5, 0xf6, 0x85, 0x5d, 0xbb, 0xb9, 0x5d, 0xb3, 0x6f, 0xfe, - 0x4c, 0xaf, 0xaf, 0xb5, 0x74, 0xeb, 0x5b, 0xd8, 0x9e, 0xeb, 0x84, 0xf6, 0x60, 0x83, 0x25, 0xc4, - 0x1b, 0x86, 0xbe, 0x32, 0xa5, 0x26, 0xc2, 0xa7, 0x3e, 0xfa, 0x08, 0xee, 0xcc, 0x2a, 0x95, 0xa3, - 0xd3, 0xdd, 0xe6, 0x0c, 0xb1, 0xd5, 0x87, 0xbd, 0x8a, 0xc7, 0xb9, 0xff, 0xe8, 0x10, 0xb6, 0x52, - 0xf2, 0x82, 0x78, 0x9c, 0xf8, 0x43, 0x65, 0xad, 0x68, 0x5f, 0x77, 0x37, 0xf3, 0x63, 0x59, 0xc6, - 0xac, 0x09, 0x6c, 0x0f, 0x58, 0xf0, 0x24, 0x25, 0xe4, 0xa7, 0x65, 0x76, 0xc9, 0x84, 0x7a, 0xb6, - 0x3c, 0x7e, 0x36, 0xab, 0x86, 0x5b, 0xc4, 0xe8, 0x03, 0x31, 0x45, 0xcc, 0x68, 0xac, 0x56, 0x49, - 0x45, 0xbd, 0x1d, 0x61, 0xcc, 0x66, 0x6e, 0x4c, 0x46, 0x65, 0xdd, 0x87, 0xfd, 0x39, 0xde, 0x62, - 0x7b, 0x46, 0xb0, 0x23, 0x77, 0xeb, 0xc7, 0xff, 0x41, 0x56, 0xef, 0x9e, 0xa0, 0x6f, 0x4d, 0xd7, - 0x38, 0x6b, 0x6a, 0x3d, 0x80, 0xfb, 0x0b, 0x38, 0x72, 0x09, 0x27, 0x7f, 0xe8, 0xb0, 0x36, 0x60, - 0x01, 0x0a, 0xe0, 0x6e, 0xf9, 0xff, 0xf2, 0xa7, 0x8b, 0x77, 0xa4, 0x7a, 0x5d, 0x4d, 0x7b, 0x39, - 0x5c, 0x31, 0xb1, 0x08, 0xb6, 0xaa, 0x57, 0xba, 0x7b, 0x6b, 0x8b, 0x0a, 0xd2, 0xfc, 0x72, 0x59, - 0x64, 0x41, 0xe7, 0xc3, 0x9d, 0xd2, 0xe5, 0xfc, 0xe4, 0xd6, 0x0e, 0xb3, 0x30, 0xf3, 0x8b, 0xa5, - 0x60, 0x05, 0xcb, 0x0b, 0xd8, 0xac, 0xac, 0xd6, 0xe1, 0xad, 0x0d, 0xca, 0x40, 0xd3, 0x59, 0x12, - 0x58, 0x70, 0x25, 0xd0, 0x9a, 0xdb, 0x98, 0xcf, 0xde, 0xe3, 0x4b, 0x19, 0x6a, 0x1e, 0x2f, 0x0d, - 0xcd, 0x19, 0xfb, 0xa7, 0x6f, 0xae, 0xdb, 0xda, 0xdb, 0xeb, 0xb6, 0xf6, 0xcf, 0x75, 0x5b, 0xfb, - 0xed, 0xa6, 0xbd, 0xf2, 0xf6, 0xa6, 0xbd, 0xf2, 0xd7, 0x4d, 0x7b, 0xe5, 0x87, 0xa3, 0x20, 0xe4, - 0x17, 0x2f, 0x47, 0xb6, 0x47, 0x23, 0xa7, 0xf4, 0xdd, 0x30, 0x79, 0xe4, 0xbc, 0x9a, 0x7e, 0xd1, - 0x5c, 0x25, 0x84, 0x8d, 0x6a, 0xf2, 0x23, 0xe0, 0xd1, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x78, - 0x28, 0x40, 0xa1, 0xf6, 0x08, 0x00, 0x00, + 0x14, 0x8e, 0x12, 0xc5, 0xb1, 0x9f, 0xdb, 0xc4, 0xde, 0xb8, 0x44, 0x51, 0x52, 0x63, 0x54, 0x20, + 0x26, 0x03, 0x12, 0x49, 0x0f, 0xcc, 0xf8, 0xe8, 0x84, 0x32, 0xed, 0xd4, 0x43, 0x46, 0x19, 0x0e, + 0x70, 0xf1, 0xac, 0xa5, 0x45, 0x51, 0x63, 0x6b, 0x35, 0xda, 0xad, 0xa7, 0xe1, 0xcc, 0x89, 0x0b, + 0x70, 0xe1, 0x37, 0x70, 0xec, 0x7f, 0xe0, 0xd2, 0x63, 0x8f, 0x9c, 0x18, 0x26, 0x39, 0xf4, 0x6f, + 0x30, 0xbb, 0x5a, 0xc9, 0xb6, 0xec, 0x64, 0xcc, 0xc0, 0xc5, 0xd2, 0xee, 0xfb, 0xf6, 0x7d, 0xdf, + 0x7e, 0xfb, 0xde, 0x5a, 0xf0, 0x70, 0x88, 0xc7, 0x38, 0x22, 0xdc, 0x11, 0x4f, 0x27, 0xc6, 0x61, + 0x12, 0x46, 0x81, 0xc3, 0x5f, 0xd9, 0x71, 0x42, 0x39, 0x45, 0x0d, 0x15, 0xb6, 0xc5, 0xd3, 0x56, + 0x61, 0xb3, 0xe9, 0x51, 0x36, 0xa2, 0xcc, 0x19, 0x60, 0x46, 0x9c, 0xf1, 0xd1, 0x80, 0x70, 0x7c, + 0xe4, 0x78, 0x34, 0x8c, 0xd2, 0x55, 0x66, 0x23, 0xa0, 0x01, 0x95, 0xaf, 0x8e, 0x78, 0x53, 0xb3, + 0xed, 0x19, 0x2a, 0x12, 0x53, 0xef, 0x82, 0x71, 0x9a, 0xe0, 0x80, 0x38, 0x24, 0xf2, 0x63, 0x1a, + 0x46, 0x5c, 0x21, 0x5b, 0x0b, 0x45, 0x25, 0x64, 0x88, 0xaf, 0x14, 0xe2, 0x43, 0xa5, 0x80, 0x71, + 0x7c, 0x29, 0x62, 0x99, 0x08, 0x35, 0x56, 0xa8, 0x3a, 0x1e, 0x85, 0x11, 0x75, 0xe4, 0x6f, 0x3a, + 0x65, 0xfd, 0xac, 0x43, 0xad, 0xc7, 0x82, 0x73, 0x8e, 0x2f, 0xc9, 0x59, 0x42, 0xc7, 0xa1, 0x4f, + 0x12, 0x64, 0xc0, 0x86, 0x97, 0x10, 0xcc, 0x69, 0x62, 0x68, 0x2d, 0xad, 0x5d, 0x71, 0xb3, 0xa1, + 0x8c, 0x5c, 0xe0, 0x30, 0x7a, 0x7a, 0x6a, 0xac, 0xaa, 0x48, 0x3a, 0x44, 0x5f, 0x40, 0x09, 0x8f, + 0xe8, 0xcb, 0x88, 0x1b, 0x6b, 0x2d, 0xad, 0x5d, 0x3d, 0xde, 0xb5, 0x53, 0x49, 0xb6, 0x30, 0xc5, + 0x56, 0x7a, 0xec, 0x13, 0x1a, 0x46, 0x5d, 0xfd, 0xcd, 0x5f, 0xef, 0xaf, 0xb8, 0x0a, 0x8e, 0xbe, + 0x82, 0x4a, 0xb6, 0x5d, 0x66, 0xe8, 0xad, 0xb5, 0x76, 0xf5, 0xf8, 0x91, 0x3d, 0x63, 0xf3, 0xb4, + 0x35, 0xf6, 0x97, 0x0a, 0xab, 0xb2, 0x4c, 0xd6, 0xa2, 0x16, 0x54, 0x03, 0x42, 0x87, 0xd4, 0xc3, + 0x3c, 0xa4, 0x91, 0xb1, 0xde, 0xd2, 0xda, 0xeb, 0xee, 0xf4, 0x14, 0x7a, 0x02, 0x9b, 0x3e, 0x19, + 0x92, 0x00, 0x73, 0xd2, 0x1f, 0x86, 0xa3, 0x90, 0x1b, 0x1b, 0xcb, 0x69, 0xbd, 0x9f, 0x2d, 0x7b, + 0x2e, 0x56, 0x21, 0x07, 0xb6, 0xf3, 0x3c, 0x1e, 0x1d, 0x8d, 0x42, 0xc6, 0x04, 0x63, 0xb9, 0xa5, + 0xb5, 0x75, 0x17, 0x65, 0xa1, 0x93, 0x3c, 0x82, 0xf6, 0xa1, 0x32, 0xc6, 0xc3, 0xd0, 0x97, 0x96, + 0x56, 0xa4, 0x71, 0x93, 0x09, 0x61, 0x2a, 0xf6, 0xfd, 0x84, 0x30, 0x66, 0x40, 0x6a, 0xaa, 0x1a, + 0xa2, 0x33, 0xa8, 0xfa, 0x84, 0x79, 0x49, 0x18, 0xcb, 0x2d, 0x55, 0xa5, 0xda, 0x47, 0x99, 0xda, + 0xec, 0x70, 0x33, 0xc1, 0xa7, 0x13, 0x68, 0xb7, 0x22, 0x74, 0xff, 0xfe, 0xee, 0xf5, 0xa1, 0xe6, + 0x4e, 0xa7, 0xe8, 0x98, 0x3f, 0xbd, 0x7b, 0x7d, 0xf8, 0x20, 0x2b, 0xa1, 0x99, 0x63, 0x7f, 0xa6, + 0x97, 0x4b, 0xb5, 0x0d, 0xcb, 0x04, 0xa3, 0x58, 0x10, 0x2e, 0x61, 0x31, 0x8d, 0x18, 0xb1, 0x7e, + 0xd4, 0x00, 0xf5, 0x58, 0xf0, 0x4d, 0xc4, 0xfe, 0x73, 0xbd, 0xcc, 0x58, 0xb2, 0x56, 0xb0, 0xa4, + 0xb3, 0x2f, 0x64, 0xee, 0x64, 0x32, 0x0b, 0x7c, 0xd6, 0x3e, 0x98, 0xf3, 0x2a, 0x72, 0x91, 0xbf, + 0xae, 0xc2, 0x56, 0x8f, 0x05, 0xae, 0x68, 0x8f, 0x33, 0x7c, 0x35, 0x22, 0x11, 0xbf, 0x43, 0x61, + 0x07, 0x4a, 0xb2, 0x91, 0x98, 0xb1, 0x2a, 0x6b, 0xcf, 0xb2, 0x17, 0xb5, 0xb8, 0x2d, 0xb3, 0x9d, + 0x13, 0x79, 0x9c, 0xae, 0x5a, 0x81, 0x3e, 0x85, 0xfa, 0x94, 0xb7, 0xe7, 0x5c, 0x20, 0x0d, 0x5d, + 0xe6, 0x9f, 0x0f, 0xa0, 0x6f, 0xa1, 0x31, 0xc4, 0x9c, 0x30, 0xde, 0x1f, 0x0c, 0xa9, 0x77, 0xd9, + 0x4f, 0x48, 0x4c, 0x13, 0xce, 0x8c, 0x75, 0xc9, 0x7b, 0xb0, 0x98, 0xf7, 0xb9, 0x5c, 0xd1, 0x15, + 0x0b, 0x5c, 0x89, 0x77, 0xd1, 0xb0, 0x38, 0xc5, 0x3a, 0xbb, 0xc2, 0xae, 0x46, 0x66, 0xd7, 0xf4, + 0xce, 0x9f, 0xe9, 0xe5, 0xb5, 0x9a, 0x6e, 0x7d, 0x0d, 0xf5, 0xb9, 0x4c, 0x68, 0x07, 0x36, 0x58, + 0x4c, 0xbc, 0x7e, 0xe8, 0x2b, 0x53, 0x4a, 0x62, 0xf8, 0xd4, 0x47, 0x1f, 0xc0, 0xbd, 0x69, 0xa5, + 0xf2, 0xe8, 0x74, 0xb7, 0x3a, 0x45, 0x6c, 0x75, 0x61, 0xa7, 0xe0, 0x71, 0xe6, 0x3f, 0x3a, 0x80, + 0xad, 0x84, 0xbc, 0x20, 0x1e, 0x27, 0x7e, 0x5f, 0x59, 0x2b, 0xd2, 0x97, 0xdd, 0xcd, 0x6c, 0x5a, + 0x2e, 0x63, 0xd6, 0x18, 0xea, 0x3d, 0x16, 0x3c, 0x49, 0x08, 0xf9, 0x61, 0x99, 0x5a, 0x32, 0xa1, + 0x9c, 0x16, 0x8f, 0x9f, 0x9e, 0x55, 0xc5, 0xcd, 0xc7, 0xe8, 0x3d, 0x71, 0x8a, 0x98, 0xd1, 0x48, + 0x95, 0x92, 0x1a, 0x75, 0xb6, 0x85, 0x31, 0x9b, 0x99, 0x31, 0x29, 0x95, 0xb5, 0x07, 0xbb, 0x73, + 0xbc, 0x79, 0xf5, 0x0c, 0x60, 0x5b, 0xd6, 0xd6, 0xf7, 0xff, 0x83, 0xac, 0xce, 0x03, 0x41, 0x5f, + 0x9b, 0x94, 0x71, 0x9a, 0xd4, 0x7a, 0x08, 0x7b, 0x0b, 0x38, 0x72, 0x09, 0x7f, 0x68, 0xd0, 0xe8, + 0xb1, 0xa0, 0x47, 0xc7, 0x79, 0x4c, 0xb6, 0xe3, 0x1d, 0x22, 0xf6, 0xa0, 0xc2, 0x12, 0xaf, 0x2f, + 0x89, 0x55, 0xa7, 0x95, 0x59, 0xe2, 0x9d, 0x88, 0xb1, 0x08, 0xfa, 0x8c, 0xab, 0x60, 0xea, 0x4f, + 0xd9, 0x67, 0x3c, 0x0d, 0x4e, 0xee, 0x6d, 0xfd, 0x5f, 0xdd, 0xdb, 0x9d, 0xa6, 0xd8, 0xdb, 0x6e, + 0xb6, 0xb7, 0x39, 0xb1, 0x56, 0x13, 0xf6, 0x17, 0x6d, 0x22, 0xdb, 0xe5, 0xf1, 0x6f, 0xeb, 0xb0, + 0xd6, 0x63, 0x01, 0x0a, 0xe0, 0xfe, 0xec, 0xbf, 0xcf, 0xc7, 0x8b, 0x3b, 0xa1, 0x78, 0x29, 0x99, + 0xf6, 0x72, 0xb8, 0xbc, 0x2e, 0x47, 0xb0, 0x55, 0xbc, 0xb8, 0xda, 0xb7, 0xa6, 0x28, 0x20, 0xcd, + 0xcf, 0x97, 0x45, 0xe6, 0x74, 0x3e, 0xdc, 0x9b, 0xb9, 0x82, 0x3e, 0xba, 0x35, 0xc3, 0x34, 0xcc, + 0xfc, 0x6c, 0x29, 0x58, 0xce, 0xf2, 0x02, 0x36, 0x0b, 0x0d, 0x74, 0x70, 0x6b, 0x82, 0x59, 0xa0, + 0xe9, 0x2c, 0x09, 0xcc, 0xb9, 0x62, 0xa8, 0xcd, 0xf5, 0xc5, 0x27, 0x77, 0xf8, 0x32, 0x0b, 0x35, + 0x8f, 0x96, 0x86, 0xe6, 0x8c, 0x0c, 0xea, 0xf3, 0x5d, 0x70, 0x78, 0x6b, 0x9e, 0x39, 0xac, 0x79, + 0xbc, 0x3c, 0x36, 0x23, 0xed, 0x9e, 0xbe, 0xb9, 0x6e, 0x6a, 0x6f, 0xaf, 0x9b, 0xda, 0xdf, 0xd7, + 0x4d, 0xed, 0x97, 0x9b, 0xe6, 0xca, 0xdb, 0x9b, 0xe6, 0xca, 0x9f, 0x37, 0xcd, 0x95, 0xef, 0x0e, + 0x83, 0x90, 0x5f, 0xbc, 0x1c, 0xd8, 0x1e, 0x1d, 0x39, 0x33, 0x9f, 0x64, 0xe3, 0xc7, 0xce, 0xab, + 0xc9, 0xc7, 0xe2, 0x55, 0x4c, 0xd8, 0xa0, 0x24, 0xbf, 0xaf, 0x1e, 0xff, 0x13, 0x00, 0x00, 0xff, + 0xff, 0x5b, 0x67, 0x92, 0xf3, 0x51, 0x0a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -721,6 +832,7 @@ type MsgClient interface { RelayPayment(ctx context.Context, in *MsgRelayPayment, opts ...grpc.CallOption) (*MsgRelayPaymentResponse, error) FreezeProvider(ctx context.Context, in *MsgFreezeProvider, opts ...grpc.CallOption) (*MsgFreezeProviderResponse, error) UnfreezeProvider(ctx context.Context, in *MsgUnfreezeProvider, opts ...grpc.CallOption) (*MsgUnfreezeProviderResponse, error) + MoveProviderStake(ctx context.Context, in *MsgMoveProviderStake, opts ...grpc.CallOption) (*MsgMoveProviderStakeResponse, error) } type msgClient struct { @@ -776,6 +888,15 @@ func (c *msgClient) UnfreezeProvider(ctx context.Context, in *MsgUnfreezeProvide return out, nil } +func (c *msgClient) MoveProviderStake(ctx context.Context, in *MsgMoveProviderStake, opts ...grpc.CallOption) (*MsgMoveProviderStakeResponse, error) { + out := new(MsgMoveProviderStakeResponse) + err := c.cc.Invoke(ctx, "/lavanet.lava.pairing.Msg/MoveProviderStake", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { StakeProvider(context.Context, *MsgStakeProvider) (*MsgStakeProviderResponse, error) @@ -783,6 +904,7 @@ type MsgServer interface { RelayPayment(context.Context, *MsgRelayPayment) (*MsgRelayPaymentResponse, error) FreezeProvider(context.Context, *MsgFreezeProvider) (*MsgFreezeProviderResponse, error) UnfreezeProvider(context.Context, *MsgUnfreezeProvider) (*MsgUnfreezeProviderResponse, error) + MoveProviderStake(context.Context, *MsgMoveProviderStake) (*MsgMoveProviderStakeResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -804,6 +926,9 @@ func (*UnimplementedMsgServer) FreezeProvider(ctx context.Context, req *MsgFreez func (*UnimplementedMsgServer) UnfreezeProvider(ctx context.Context, req *MsgUnfreezeProvider) (*MsgUnfreezeProviderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UnfreezeProvider not implemented") } +func (*UnimplementedMsgServer) MoveProviderStake(ctx context.Context, req *MsgMoveProviderStake) (*MsgMoveProviderStakeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MoveProviderStake not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -899,6 +1024,24 @@ func _Msg_UnfreezeProvider_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _Msg_MoveProviderStake_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMoveProviderStake) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MoveProviderStake(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lavanet.lava.pairing.Msg/MoveProviderStake", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MoveProviderStake(ctx, req.(*MsgMoveProviderStake)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "lavanet.lava.pairing.Msg", HandlerType: (*MsgServer)(nil), @@ -923,6 +1066,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "UnfreezeProvider", Handler: _Msg_UnfreezeProvider_Handler, }, + { + MethodName: "MoveProviderStake", + Handler: _Msg_MoveProviderStake_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "lavanet/lava/pairing/tx.proto", @@ -1387,6 +1534,83 @@ func (m *MsgUnfreezeProviderResponse) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } +func (m *MsgMoveProviderStake) 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 *MsgMoveProviderStake) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMoveProviderStake) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.DstChain) > 0 { + i -= len(m.DstChain) + copy(dAtA[i:], m.DstChain) + i = encodeVarintTx(dAtA, i, uint64(len(m.DstChain))) + i-- + dAtA[i] = 0x1a + } + if len(m.SrcChain) > 0 { + i -= len(m.SrcChain) + copy(dAtA[i:], m.SrcChain) + i = encodeVarintTx(dAtA, i, uint64(len(m.SrcChain))) + i-- + dAtA[i] = 0x12 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgMoveProviderStakeResponse) 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 *MsgMoveProviderStakeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMoveProviderStakeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -1597,6 +1821,38 @@ func (m *MsgUnfreezeProviderResponse) Size() (n int) { return n } +func (m *MsgMoveProviderStake) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.SrcChain) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.DstChain) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgMoveProviderStakeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2911,6 +3167,235 @@ func (m *MsgUnfreezeProviderResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgMoveProviderStake) 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 ErrIntOverflowTx + } + 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: MsgMoveProviderStake: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMoveProviderStake: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + 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 ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SrcChain", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + 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 ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SrcChain = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DstChain", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + 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 ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DstChain = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMoveProviderStakeResponse) 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 ErrIntOverflowTx + } + 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: MsgMoveProviderStakeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMoveProviderStakeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/projects/keeper/creation.go b/x/projects/keeper/creation.go index 423aed7227..a3f1c75741 100644 --- a/x/projects/keeper/creation.go +++ b/x/projects/keeper/creation.go @@ -191,7 +191,7 @@ func (k Keeper) registerKey(ctx sdk.Context, key types.ProjectKey, project *type // check that the developer key is valid, and that it does not already // belong to a different project. - if found && devkeyData.ProjectID != project.Index { + if found && devkeyData.ProjectID != project.GetIndex() { return utils.LavaFormatWarning("failed to register key", fmt.Errorf("key already exists"), utils.Attribute{Key: "key", Value: key.Key}, @@ -254,10 +254,9 @@ func (k Keeper) unregisterKey(ctx sdk.Context, key types.ProjectKey, project *ty // the developer key belongs to a different project if devkeyData.ProjectID != project.GetIndex() { return utils.LavaFormatWarning("failed to unregister key", legacyerrors.ErrNotFound, - utils.Attribute{Key: "projectID", Value: project.Index}, + utils.Attribute{Key: "projectID", Value: project.GetIndex()}, utils.Attribute{Key: "key", Value: key.Key}, utils.Attribute{Key: "keyTypes", Value: key.Kinds}, - utils.Attribute{Key: "projectID", Value: project.GetIndex()}, utils.Attribute{Key: "otherID", Value: devkeyData.ProjectID}, ) } diff --git a/x/projects/types/project.go b/x/projects/types/project.go index d1f7f5e3a2..9289e67257 100644 --- a/x/projects/types/project.go +++ b/x/projects/types/project.go @@ -82,6 +82,9 @@ func (project *Project) GetKey(key string) ProjectKey { } func (project *Project) AppendKey(key ProjectKey) bool { + if project == nil { + return false + } for i, projectKey := range project.ProjectKeys { if projectKey.Key == key.Key { project.ProjectKeys[i].Kinds |= key.Kinds diff --git a/x/rewards/keeper/iprpc.go b/x/rewards/keeper/iprpc.go index 8ac287e452..90d94380c9 100644 --- a/x/rewards/keeper/iprpc.go +++ b/x/rewards/keeper/iprpc.go @@ -132,7 +132,6 @@ func (k Keeper) distributeIprpcRewards(ctx sdk.Context, iprpcReward types.IprpcR k.handleNoIprpcRewardToProviders(ctx, iprpcReward.SpecFunds) return } - leftovers := sdk.NewCoins() for _, specFund := range iprpcReward.SpecFunds { details := map[string]string{} @@ -178,9 +177,10 @@ func (k Keeper) distributeIprpcRewards(ctx sdk.Context, iprpcReward types.IprpcR UsedReward = UsedRewardTemp // reward the provider - _, _, err := k.dualstakingKeeper.RewardProvidersAndDelegators(ctx, providerCU.Provider, specFund.Spec, providerIprpcReward, string(types.IprpcPoolName), false, false, false) + _, err := k.dualstakingKeeper.RewardProvidersAndDelegators(ctx, providerCU.Provider, specFund.Spec, providerIprpcReward, string(types.IprpcPoolName), false, false, false) if err != nil { - utils.LavaFormatError("failed to send iprpc rewards to provider", err, utils.LogAttr("provider", providerCU)) + // failed sending the rewards, add the claimable rewards to the leftovers that will be transferred to the community pool + utils.LavaFormatPanic("failed to send iprpc rewards to provider", err, utils.LogAttr("provider", providerCU)) } details[providerCU.Provider] = fmt.Sprintf("cu: %d reward: %s", providerCU.CU, providerIprpcReward.String()) } diff --git a/x/rewards/keeper/providers.go b/x/rewards/keeper/providers.go index e9a45ab093..43dc4b42f9 100644 --- a/x/rewards/keeper/providers.go +++ b/x/rewards/keeper/providers.go @@ -88,7 +88,7 @@ func (k Keeper) distributeMonthlyBonusRewards(ctx sdk.Context) { return } // now give the reward the provider contributor and delegators - _, _, err := k.dualstakingKeeper.RewardProvidersAndDelegators(ctx, basepay.Provider, basepay.ChainId, sdk.NewCoins(sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), reward)), string(types.ProviderRewardsDistributionPool), false, false, false) + _, err := k.dualstakingKeeper.RewardProvidersAndDelegators(ctx, basepay.Provider, basepay.ChainId, sdk.NewCoins(sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), reward)), string(types.ProviderRewardsDistributionPool), false, false, false) if err != nil { utils.LavaFormatError("failed to send bonus rewards to provider", err, utils.LogAttr("provider", basepay.Provider)) } @@ -178,10 +178,14 @@ func (k Keeper) specProvidersBasePay(ctx sdk.Context, chainID string, pop bool) } totalBasePay := math.ZeroInt() + stakedBasePays := []types.BasePayWithIndex{} for _, basepay := range basepays { - totalBasePay = totalBasePay.Add(basepay.BasePay.Total) + if _, found := k.epochstorage.GetStakeEntryCurrent(ctx, basepay.ChainId, basepay.Provider); found { + totalBasePay = totalBasePay.Add(basepay.BasePay.Total) + stakedBasePays = append(stakedBasePays, basepay) + } } - return basepays, totalBasePay + return stakedBasePays, totalBasePay } // ContributeToValidatorsAndCommunityPool transfers some of the providers' rewards to the validators and community pool diff --git a/x/rewards/keeper/providers_test.go b/x/rewards/keeper/providers_test.go index 5ed1c58d52..0e4c1e906e 100644 --- a/x/rewards/keeper/providers_test.go +++ b/x/rewards/keeper/providers_test.go @@ -271,10 +271,9 @@ func Test2SpecsZeroShares(t *testing.T) { res, err := ts.QueryDualstakingDelegatorRewards(providerAcc.GetVaultAddr(), providerAcc.Addr.String(), "") require.NoError(t, err) - require.Len(t, res.Rewards, 2) + require.Len(t, res.Rewards, 1) expectedReward, _, _ := ts.DeductParticipationFees(ts.plan.Price.Amount) - require.Equal(t, expectedReward, res.Rewards[0].Amount.AmountOf(ts.BondDenom())) - require.Equal(t, expectedReward, res.Rewards[1].Amount.AmountOf(ts.BondDenom())) + require.Equal(t, expectedReward.MulRaw(2), res.Rewards[0].Amount.AmountOf(ts.BondDenom())) _, err = ts.TxDualstakingClaimRewards(providerAcc.GetVaultAddr(), "") require.NoError(t, err) @@ -287,7 +286,6 @@ func Test2SpecsZeroShares(t *testing.T) { require.NoError(t, err) require.Len(t, res.Rewards, 1) require.Equal(t, distBalance.QuoRaw(int64(ts.Keepers.Rewards.MaxRewardBoost(ts.Ctx))), res.Rewards[0].Amount.AmountOf(ts.BondDenom())) - require.Equal(t, res.Rewards[0].ChainId, ts.specs[0].Index) _, err = ts.TxDualstakingClaimRewards(providerAcc.GetVaultAddr(), providerAcc.Addr.String()) require.NoError(t, err) } @@ -303,11 +301,12 @@ func Test2SpecsDoubleShares(t *testing.T) { spec2.Shares *= 2 ts.AddSpec(spec2.Index, spec2) - providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, 2*testBalance) - err := ts.StakeProvider(providerAcc.GetVaultAddr(), providerAcc.Addr.String(), ts.specs[0], testBalance) + providerAcc1, _ := ts.AddAccount(common.PROVIDER, 1, 2*testBalance) + err := ts.StakeProvider(providerAcc1.GetVaultAddr(), providerAcc1.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) - err = ts.StakeProvider(providerAcc.GetVaultAddr(), providerAcc.Addr.String(), spec2, testBalance) + providerAcc2, _ := ts.AddAccount(common.PROVIDER, 2, 2*testBalance) + err = ts.StakeProvider(providerAcc2.GetVaultAddr(), providerAcc2.Addr.String(), spec2, testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -316,7 +315,7 @@ func Test2SpecsDoubleShares(t *testing.T) { _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.NoError(t, err) - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()) + msg := ts.SendRelay(providerAcc1.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -324,7 +323,7 @@ func Test2SpecsDoubleShares(t *testing.T) { _, err = ts.TxSubscriptionBuy(consumerAcc2.Addr.String(), consumerAcc2.Addr.String(), ts.plan.Index, 1, false, false) require.NoError(t, err) - msg = ts.SendRelay(providerAcc.Addr.String(), consumerAcc2, []string{spec2.Index}, ts.plan.Price.Amount.Uint64()) + msg = ts.SendRelay(providerAcc2.Addr.String(), consumerAcc2, []string{spec2.Index}, ts.plan.Price.Amount.Uint64()) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -333,25 +332,39 @@ func Test2SpecsDoubleShares(t *testing.T) { ts.AdvanceEpoch() ts.AdvanceBlocks(ts.BlocksToSave() + 1) - res, err := ts.QueryDualstakingDelegatorRewards(providerAcc.GetVaultAddr(), providerAcc.Addr.String(), "") + res, err := ts.QueryDualstakingDelegatorRewards(providerAcc1.GetVaultAddr(), providerAcc1.Addr.String(), "") require.NoError(t, err) - require.Len(t, res.Rewards, 2) + require.Len(t, res.Rewards, 1) expectedReward, _, _ := ts.DeductParticipationFees(ts.plan.Price.Amount) require.Equal(t, expectedReward, res.Rewards[0].Amount.AmountOf(ts.BondDenom())) - require.Equal(t, expectedReward, res.Rewards[1].Amount.AmountOf(ts.BondDenom())) - _, err = ts.TxDualstakingClaimRewards(providerAcc.GetVaultAddr(), "") + _, err = ts.TxDualstakingClaimRewards(providerAcc1.GetVaultAddr(), "") + require.NoError(t, err) + + res, err = ts.QueryDualstakingDelegatorRewards(providerAcc2.GetVaultAddr(), providerAcc2.Addr.String(), "") + require.NoError(t, err) + require.Len(t, res.Rewards, 1) + expectedReward, _, _ = ts.DeductParticipationFees(ts.plan.Price.Amount) + require.Equal(t, expectedReward, res.Rewards[0].Amount.AmountOf(ts.BondDenom())) + _, err = ts.TxDualstakingClaimRewards(providerAcc2.GetVaultAddr(), "") require.NoError(t, err) // now the provider should get all of the provider allocation ts.AdvanceMonths(1) ts.AdvanceEpoch() - res, err = ts.QueryDualstakingDelegatorRewards(providerAcc.GetVaultAddr(), providerAcc.Addr.String(), "") + res1, err := ts.QueryDualstakingDelegatorRewards(providerAcc1.GetVaultAddr(), providerAcc1.Addr.String(), "") require.NoError(t, err) - require.Len(t, res.Rewards, 2) - require.Equal(t, res.Rewards[0].Amount.AmountOf(ts.BondDenom()).QuoRaw(2), res.Rewards[1].Amount.AmountOf(ts.BondDenom())) - _, err = ts.TxDualstakingClaimRewards(providerAcc.GetVaultAddr(), providerAcc.Addr.String()) + require.Len(t, res1.Rewards, 1) + _, err = ts.TxDualstakingClaimRewards(providerAcc1.GetVaultAddr(), providerAcc1.Addr.String()) + require.NoError(t, err) + + res2, err := ts.QueryDualstakingDelegatorRewards(providerAcc2.GetVaultAddr(), providerAcc2.Addr.String(), "") require.NoError(t, err) + require.Len(t, res1.Rewards, 1) + _, err = ts.TxDualstakingClaimRewards(providerAcc2.GetVaultAddr(), providerAcc2.Addr.String()) + require.NoError(t, err) + + require.Equal(t, res1.Rewards[0].Amount.AmountOf(ts.BondDenom()), res2.Rewards[0].Amount.AmountOf(ts.BondDenom()).QuoRaw(2)) } // in this test we setup 3 providers, each with different cu used (-> 3 different rewards from the plan) (1,2,4) diff --git a/x/rewards/types/expected_keepers.go b/x/rewards/types/expected_keepers.go index a79582f6d4..989c7b78ea 100644 --- a/x/rewards/types/expected_keepers.go +++ b/x/rewards/types/expected_keepers.go @@ -53,7 +53,7 @@ type StakingKeeper interface { } type DualStakingKeeper interface { - RewardProvidersAndDelegators(ctx sdk.Context, providerAddr string, chainID string, totalReward sdk.Coins, senderModule string, calcOnlyProvider bool, calcOnlyDelegators bool, calcOnlyContributor bool) (providerReward sdk.Coins, totalRewards sdk.Coins, err error) + RewardProvidersAndDelegators(ctx sdk.Context, providerAddr string, chainID string, totalReward sdk.Coins, senderModule string, calcOnlyProvider bool, calcOnlyDelegators bool, calcOnlyContributor bool) (providerReward sdk.Coins, err error) // Methods imported from bank should be defined here } diff --git a/x/spec/keeper/spec.go b/x/spec/keeper/spec.go index 59c0a5e6ce..b52e77fe1a 100644 --- a/x/spec/keeper/spec.go +++ b/x/spec/keeper/spec.go @@ -107,6 +107,9 @@ func (k Keeper) RefreshSpec(ctx sdk.Context, spec types.Spec, ancestors []types. } if details, err := spec.ValidateSpec(k.MaxCU(ctx)); err != nil { + if details != nil { + details = map[string]string{} + } details["invalidates"] = spec.Index attrs := utils.StringMapToAttributes(details) return nil, utils.LavaFormatWarning("spec refresh failed (invalidate)", err, attrs...) @@ -137,6 +140,9 @@ func (k Keeper) doExpandSpec( inherit *map[string]bool, details string, ) (string, error) { + if spec == nil { + return "", fmt.Errorf("doExpandSpec: spec is nil") + } parentsCollections := map[types.CollectionData][]*types.ApiCollection{} if len(spec.Imports) != 0 { diff --git a/x/spec/types/api_collection.go b/x/spec/types/api_collection.go index 06561b16bf..7df10e64ee 100644 --- a/x/spec/types/api_collection.go +++ b/x/spec/types/api_collection.go @@ -65,6 +65,9 @@ func (apic *ApiCollection) InheritAllFields(myCollections map[CollectionData]*Ap // changes in place inside the apic // nil merge maps means not to combine that field func (apic *ApiCollection) CombineWithOthers(others []*ApiCollection, combineWithDisabled, allowOverwrite bool) (err error) { + if apic == nil { + return fmt.Errorf("CombineWithOthers: API collection is nil") + } mergedApis := map[string]interface{}{} mergedHeaders := map[string]interface{}{} mergedParsers := map[string]interface{}{} diff --git a/x/spec/types/api_collection.pb.go b/x/spec/types/api_collection.pb.go index 5591ce0b28..2cb76295ff 100644 --- a/x/spec/types/api_collection.pb.go +++ b/x/spec/types/api_collection.pb.go @@ -795,10 +795,11 @@ func (m *Api) GetParsers() []GenericParser { } type ParseDirective struct { - FunctionTag FUNCTION_TAG `protobuf:"varint,1,opt,name=function_tag,json=functionTag,proto3,enum=lavanet.lava.spec.FUNCTION_TAG" json:"function_tag,omitempty"` - FunctionTemplate string `protobuf:"bytes,2,opt,name=function_template,json=functionTemplate,proto3" json:"function_template,omitempty"` - ResultParsing BlockParser `protobuf:"bytes,3,opt,name=result_parsing,json=resultParsing,proto3" json:"result_parsing"` - ApiName string `protobuf:"bytes,4,opt,name=api_name,json=apiName,proto3" json:"api_name,omitempty"` + FunctionTag FUNCTION_TAG `protobuf:"varint,1,opt,name=function_tag,json=functionTag,proto3,enum=lavanet.lava.spec.FUNCTION_TAG" json:"function_tag,omitempty"` + FunctionTemplate string `protobuf:"bytes,2,opt,name=function_template,json=functionTemplate,proto3" json:"function_template,omitempty"` + ResultParsing BlockParser `protobuf:"bytes,3,opt,name=result_parsing,json=resultParsing,proto3" json:"result_parsing"` + ApiName string `protobuf:"bytes,4,opt,name=api_name,json=apiName,proto3" json:"api_name,omitempty"` + Parsers []GenericParser `protobuf:"bytes,5,rep,name=parsers,proto3" json:"parsers"` } func (m *ParseDirective) Reset() { *m = ParseDirective{} } @@ -862,6 +863,13 @@ func (m *ParseDirective) GetApiName() string { return "" } +func (m *ParseDirective) GetParsers() []GenericParser { + if m != nil { + return m.Parsers + } + return nil +} + type BlockParser struct { ParserArg []string `protobuf:"bytes,1,rep,name=parser_arg,json=parserArg,proto3" json:"parser_arg,omitempty"` ParserFunc PARSER_FUNC `protobuf:"varint,2,opt,name=parser_func,json=parserFunc,proto3,enum=lavanet.lava.spec.PARSER_FUNC" json:"parser_func,omitempty"` @@ -1100,108 +1108,108 @@ func init() { } var fileDescriptor_c9f7567a181f534f = []byte{ - // 1606 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x57, 0xcf, 0x6f, 0xdb, 0xc8, + // 1613 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xcf, 0x6f, 0xdb, 0xc8, 0x15, 0x36, 0x25, 0xea, 0xd7, 0xb3, 0x24, 0x4f, 0x26, 0x6e, 0xaa, 0x4d, 0xb3, 0x96, 0xcb, 0xdd, 0x76, 0x03, 0x2f, 0x6a, 0xa3, 0x0e, 0x0a, 0x14, 0x8b, 0x16, 0x2d, 0x25, 0xd1, 0x89, 0x12, 0x59, 0x32, 0x46, 0xb4, 0x5b, 0xf7, 0x42, 0x8c, 0xa9, 0xb1, 0x3c, 0x58, 0x8a, 0x24, 0xc8, 0xa1, 0x1b, - 0x9f, 0xfb, 0x0f, 0xf4, 0x50, 0xa0, 0xff, 0x42, 0x81, 0x02, 0x05, 0xfa, 0x5f, 0xec, 0x71, 0x6f, - 0xed, 0xc9, 0x28, 0x92, 0x43, 0xd1, 0x1c, 0x73, 0x29, 0x7a, 0x28, 0x50, 0xcc, 0x90, 0xfa, 0xc1, - 0x44, 0x71, 0x91, 0x93, 0x34, 0xdf, 0x7c, 0xf3, 0xcd, 0x9b, 0xf7, 0xde, 0x7c, 0x23, 0xc1, 0x0f, - 0x3d, 0x7a, 0x4d, 0x7d, 0x26, 0x0e, 0xe4, 0xe7, 0x41, 0x1c, 0x32, 0xf7, 0x80, 0x86, 0xdc, 0x71, - 0x03, 0xcf, 0x63, 0xae, 0xe0, 0x81, 0xbf, 0x1f, 0x46, 0x81, 0x08, 0xf0, 0xbd, 0x8c, 0xb7, 0x2f, - 0x3f, 0xf7, 0x25, 0xef, 0xe1, 0xf6, 0x34, 0x98, 0x06, 0x6a, 0xf6, 0x40, 0x7e, 0x4b, 0x89, 0xc6, - 0x7f, 0x8b, 0xd0, 0x30, 0x43, 0xde, 0x5d, 0x08, 0xe0, 0x16, 0x54, 0x98, 0x4f, 0x2f, 0x3c, 0x36, - 0x69, 0x69, 0xbb, 0xda, 0xe3, 0x2a, 0x99, 0x0f, 0xf1, 0x09, 0x6c, 0x2d, 0x37, 0x72, 0x26, 0x54, - 0xd0, 0x56, 0x61, 0x57, 0x7b, 0xbc, 0x79, 0xf8, 0xfd, 0xfd, 0xf7, 0xb6, 0xdb, 0x5f, 0x2a, 0xf6, - 0xa8, 0xa0, 0x1d, 0xfd, 0x9b, 0xdb, 0xf6, 0x06, 0x69, 0xba, 0x39, 0x14, 0xef, 0x81, 0x4e, 0x43, - 0x1e, 0xb7, 0x8a, 0xbb, 0xc5, 0xc7, 0x9b, 0x87, 0x0f, 0xd6, 0xc8, 0x98, 0x21, 0x27, 0x8a, 0x83, - 0x9f, 0x40, 0xe5, 0x8a, 0xd1, 0x09, 0x8b, 0xe2, 0x96, 0xae, 0xe8, 0x9f, 0xac, 0xa1, 0x3f, 0x53, - 0x0c, 0x32, 0x67, 0xe2, 0x01, 0x20, 0xee, 0x5f, 0xb1, 0x88, 0x0b, 0xea, 0xbb, 0xcc, 0x51, 0x9b, - 0x95, 0xd4, 0xea, 0xff, 0x1f, 0x33, 0xd9, 0x5a, 0x59, 0x6a, 0xca, 0x10, 0x06, 0x80, 0x42, 0x1a, - 0xc5, 0xcc, 0x99, 0xf0, 0x48, 0xf2, 0xae, 0x59, 0xdc, 0x2a, 0x7f, 0x50, 0xed, 0x44, 0x52, 0x7b, - 0x73, 0x26, 0xd9, 0x0a, 0x73, 0xe3, 0x18, 0xff, 0x0c, 0x80, 0xbd, 0x14, 0xcc, 0x8f, 0x79, 0xe0, - 0xc7, 0xad, 0x8a, 0xd2, 0x79, 0xb4, 0x46, 0xc7, 0x9a, 0x93, 0xc8, 0x0a, 0x1f, 0x5b, 0xd0, 0xb8, - 0x66, 0x11, 0xbf, 0xe4, 0x2e, 0x15, 0x4a, 0xa0, 0xaa, 0x04, 0xda, 0x6b, 0x04, 0xce, 0x56, 0x78, - 0x24, 0xbf, 0xca, 0xf8, 0x2d, 0xd4, 0x16, 0xfa, 0x18, 0x83, 0xee, 0xd3, 0x19, 0x53, 0x75, 0xaf, - 0x11, 0xf5, 0x1d, 0x7f, 0x09, 0x7a, 0x94, 0x78, 0xac, 0x55, 0x54, 0x95, 0xfe, 0xee, 0x1a, 0x79, - 0x92, 0x78, 0x8c, 0x28, 0x12, 0xfe, 0x0c, 0x1a, 0x6e, 0xe2, 0xcc, 0x12, 0x4f, 0xf0, 0xd0, 0xe3, - 0x2c, 0x6a, 0xe9, 0xbb, 0xda, 0x63, 0x9d, 0xd4, 0xdd, 0xe4, 0x78, 0x81, 0x3d, 0xd7, 0xab, 0x05, - 0x54, 0x34, 0x1e, 0x81, 0x2e, 0x17, 0xe2, 0x6d, 0x28, 0x5d, 0x78, 0x81, 0xfb, 0xb5, 0xda, 0x54, - 0x27, 0xe9, 0xc0, 0xf8, 0xb3, 0x06, 0xf5, 0xd5, 0xb0, 0xd7, 0x86, 0xf6, 0x1c, 0xb6, 0xde, 0x29, - 0xc7, 0x1d, 0xfd, 0xf8, 0x4e, 0x35, 0x9a, 0xf9, 0x6a, 0xe0, 0x9f, 0x40, 0xf9, 0x9a, 0x7a, 0x09, - 0x9b, 0xf7, 0xe2, 0xa7, 0x1f, 0x92, 0x38, 0x93, 0x2c, 0x92, 0x91, 0x9f, 0xeb, 0x55, 0x1d, 0x95, - 0x8c, 0xff, 0x68, 0x00, 0xcb, 0x49, 0xfc, 0x08, 0x6a, 0x8b, 0x42, 0x65, 0x01, 0x2f, 0x01, 0xfc, - 0x03, 0x68, 0xb2, 0x97, 0x21, 0x73, 0x05, 0x9b, 0x38, 0x4a, 0x45, 0x05, 0x5d, 0x23, 0x8d, 0x39, - 0x9a, 0x8a, 0x7c, 0x01, 0x5b, 0x1e, 0x15, 0x2c, 0x16, 0xce, 0x84, 0xc7, 0xaa, 0x05, 0x55, 0x09, - 0x74, 0xd2, 0x4c, 0xe1, 0x5e, 0x86, 0xe2, 0x21, 0x54, 0x63, 0x26, 0x8b, 0x2a, 0x6e, 0x54, 0xba, - 0x9b, 0x87, 0x87, 0x77, 0xc6, 0x9e, 0x6b, 0x87, 0x71, 0xb6, 0x92, 0x2c, 0x34, 0x8c, 0x1f, 0xc1, - 0xf6, 0x3a, 0x06, 0xae, 0x82, 0x7e, 0x44, 0xb9, 0x87, 0x36, 0xf0, 0x26, 0x54, 0x7e, 0x45, 0x23, - 0x9f, 0xfb, 0x53, 0xa4, 0x19, 0x7f, 0x2d, 0x40, 0x33, 0x7f, 0x6f, 0xf0, 0x19, 0x34, 0xa4, 0x29, - 0x71, 0x5f, 0xb0, 0xe8, 0x92, 0xba, 0x59, 0xd1, 0x3a, 0x3f, 0x7e, 0x73, 0xdb, 0xce, 0x4f, 0xbc, - 0xbd, 0x6d, 0x3f, 0x9a, 0xd1, 0x30, 0x16, 0x51, 0xe2, 0x8a, 0x24, 0x62, 0x5f, 0x19, 0xb9, 0x69, - 0x83, 0xd4, 0x69, 0xc8, 0xfb, 0xf3, 0xa1, 0xd4, 0x55, 0x73, 0x3e, 0xf5, 0x9c, 0x90, 0x8a, 0xab, - 0x34, 0x71, 0xa9, 0x6e, 0x6e, 0xe2, 0x7d, 0xdd, 0xdc, 0xb4, 0x41, 0xea, 0xf3, 0xf1, 0x09, 0x15, - 0x57, 0xf8, 0x09, 0xe8, 0xe2, 0x26, 0x4c, 0xf3, 0x5b, 0xeb, 0xb4, 0xdf, 0xdc, 0xb6, 0xd5, 0xf8, - 0xed, 0x6d, 0xfb, 0x7e, 0x5e, 0x45, 0xa2, 0x06, 0x51, 0x93, 0xf8, 0x2b, 0x28, 0xd3, 0xc9, 0xc4, - 0x09, 0x7c, 0x95, 0xf4, 0x5a, 0xe7, 0xb3, 0x37, 0xb7, 0xed, 0x0c, 0x79, 0x7b, 0xdb, 0xfe, 0xce, - 0x3b, 0xc7, 0x52, 0xb8, 0x41, 0x4a, 0x74, 0x32, 0x19, 0xf9, 0xc6, 0x3f, 0x35, 0x28, 0xa7, 0x4e, - 0xb5, 0xb6, 0xaf, 0x7f, 0x0a, 0xfa, 0xd7, 0xdc, 0x9f, 0xa8, 0xe3, 0x35, 0x0f, 0x3f, 0xff, 0xa0, - 0xcd, 0x65, 0x1f, 0xf6, 0x4d, 0xc8, 0x88, 0x5a, 0x81, 0x3b, 0x50, 0xbf, 0x4c, 0xfc, 0xd4, 0x9f, - 0x05, 0x9d, 0xaa, 0x13, 0x35, 0xd7, 0x7a, 0xc2, 0xd1, 0xe9, 0xb0, 0x6b, 0xf7, 0x47, 0x43, 0xc7, - 0x36, 0x9f, 0x92, 0xcd, 0xf9, 0x22, 0x9b, 0x4e, 0x8d, 0x17, 0x00, 0x4b, 0x5d, 0xdc, 0x80, 0x5a, - 0x48, 0xe3, 0xd8, 0x89, 0x99, 0x3f, 0x41, 0x1b, 0xb8, 0x09, 0xa0, 0x86, 0x11, 0x0b, 0xbd, 0x1b, - 0xa4, 0x2d, 0xa6, 0x2f, 0x02, 0x71, 0x85, 0x0a, 0x78, 0x0b, 0x36, 0xd5, 0x90, 0x4f, 0xfd, 0x20, - 0x62, 0xa8, 0x68, 0xfc, 0xbb, 0x00, 0x45, 0x33, 0xe4, 0x77, 0x3c, 0x2a, 0xf3, 0x04, 0x14, 0x56, - 0x12, 0x20, 0x6d, 0x24, 0x98, 0x85, 0x89, 0x60, 0x4e, 0xe2, 0x73, 0x11, 0x67, 0x9d, 0x5f, 0xcf, - 0xc0, 0x53, 0x89, 0xe1, 0x7d, 0xb8, 0xcf, 0x5e, 0x8a, 0x88, 0x3a, 0x79, 0x6a, 0xea, 0x38, 0xf7, - 0xd4, 0x54, 0x77, 0x95, 0x6f, 0x42, 0xd5, 0xa5, 0x82, 0x4d, 0x83, 0xe8, 0xa6, 0x55, 0x56, 0x36, - 0xb1, 0x2e, 0x2f, 0xe3, 0x90, 0xb9, 0xdd, 0x8c, 0x96, 0x3d, 0x5a, 0x8b, 0x65, 0xb8, 0x0f, 0x0d, - 0x65, 0x4f, 0x8e, 0x34, 0x0f, 0xee, 0x4f, 0x5b, 0x15, 0xa5, 0xb3, 0xb3, 0x46, 0xa7, 0x23, 0x79, - 0xea, 0xd2, 0x45, 0x99, 0x4c, 0xfd, 0x62, 0x0e, 0x71, 0x7f, 0x8a, 0x3f, 0x05, 0x10, 0x7c, 0xc6, - 0x82, 0x44, 0x38, 0x33, 0xe9, 0xdd, 0x32, 0xe8, 0x5a, 0x86, 0x1c, 0xc7, 0xf8, 0x97, 0x50, 0x51, - 0x06, 0x15, 0xc5, 0xad, 0x9a, 0xf2, 0xa3, 0xdd, 0x35, 0x7b, 0x3c, 0x65, 0x3e, 0x8b, 0xb8, 0x9b, - 0xdb, 0x65, 0xbe, 0xcc, 0xf8, 0x97, 0x06, 0xcd, 0xbc, 0xe7, 0xbd, 0xd7, 0x1d, 0xda, 0xc7, 0x77, - 0x07, 0xfe, 0x12, 0xee, 0x2d, 0x35, 0xd8, 0x2c, 0x94, 0x66, 0x94, 0xd5, 0x0e, 0x2d, 0x78, 0x19, - 0x8e, 0x5f, 0x40, 0x33, 0x62, 0x71, 0xe2, 0x89, 0x45, 0xc2, 0x8a, 0x1f, 0x91, 0xb0, 0x46, 0xba, - 0x76, 0x9e, 0xb1, 0x4f, 0xa0, 0x2a, 0xdd, 0x41, 0x35, 0x8b, 0xba, 0x72, 0xa4, 0x42, 0x43, 0x3e, - 0xa4, 0x33, 0x66, 0xfc, 0x45, 0x83, 0xcd, 0x95, 0xf5, 0x32, 0xb9, 0x69, 0x1a, 0x1c, 0x1a, 0xc9, - 0x63, 0x16, 0xa5, 0x03, 0xa7, 0x88, 0x19, 0x4d, 0xf1, 0x2f, 0x64, 0x97, 0xaa, 0x69, 0x19, 0x71, - 0x76, 0xcd, 0xd6, 0xc5, 0x74, 0x62, 0x92, 0xb1, 0x45, 0x1c, 0x99, 0x0d, 0x92, 0x29, 0x1e, 0x25, - 0xbe, 0x2b, 0xfb, 0x73, 0xc2, 0x2e, 0xa9, 0x3c, 0x58, 0xea, 0xe0, 0xca, 0x39, 0x48, 0x3d, 0x03, - 0x53, 0x03, 0x7f, 0x08, 0x55, 0xe6, 0xbb, 0xc1, 0x44, 0x1e, 0x3b, 0x8d, 0x77, 0x31, 0x36, 0xfe, - 0xa8, 0x41, 0x23, 0x57, 0xbd, 0x45, 0xc8, 0xa9, 0xb1, 0x65, 0x8f, 0x86, 0x42, 0x94, 0x45, 0x6d, - 0x43, 0x69, 0xf5, 0xad, 0x48, 0x07, 0xf8, 0xe7, 0xf3, 0x45, 0x0b, 0xfb, 0xba, 0xf3, 0x1c, 0xf6, - 0xf9, 0x89, 0x95, 0x89, 0xaa, 0xbb, 0x8d, 0xb3, 0xa7, 0x3d, 0x8d, 0x4e, 0x7d, 0x57, 0x0f, 0xef, - 0xea, 0x1d, 0xc0, 0x9f, 0xcb, 0xb3, 0x0a, 0x16, 0xcd, 0xb8, 0xcf, 0x63, 0xc1, 0xdd, 0xec, 0xfe, - 0xe6, 0x41, 0x19, 0x9f, 0x17, 0xb8, 0xd4, 0x53, 0xf1, 0x55, 0x49, 0x3a, 0xc0, 0x06, 0xd4, 0xe3, - 0xe4, 0x22, 0x76, 0x23, 0x1e, 0xca, 0xbe, 0x50, 0x11, 0x56, 0x49, 0x0e, 0x93, 0x69, 0x8a, 0x05, - 0x15, 0xec, 0x32, 0xf1, 0x54, 0x20, 0x0d, 0xb2, 0x18, 0xe3, 0x36, 0x6c, 0x5e, 0x51, 0x7f, 0xca, - 0xfd, 0xa9, 0xfc, 0xe5, 0xd6, 0x2a, 0xa9, 0xe5, 0x90, 0x41, 0x66, 0xc8, 0xf7, 0x0c, 0xa8, 0x59, - 0xbf, 0xb6, 0xad, 0xe1, 0xb8, 0x3f, 0x1a, 0xca, 0x07, 0x6a, 0x38, 0x1a, 0x5a, 0xe9, 0x03, 0x65, - 0x92, 0xee, 0xb3, 0xfe, 0x99, 0x85, 0xb4, 0xbd, 0xbf, 0x69, 0x50, 0x5f, 0xed, 0x67, 0x5c, 0x87, - 0x6a, 0xaf, 0x3f, 0x36, 0x3b, 0x03, 0xab, 0x87, 0x36, 0x30, 0x82, 0xfa, 0x53, 0xcb, 0x76, 0x3a, - 0x83, 0x51, 0xf7, 0xc5, 0xf0, 0xf4, 0x18, 0x69, 0x78, 0x1b, 0xd0, 0x02, 0x71, 0x3a, 0xe7, 0x8e, - 0x44, 0x0b, 0xf8, 0x21, 0x3c, 0x18, 0x5b, 0xb6, 0x33, 0x30, 0x6d, 0x6b, 0x6c, 0x3b, 0xfd, 0xa1, - 0x73, 0x6c, 0xd9, 0x66, 0xcf, 0xb4, 0x4d, 0x54, 0xc4, 0x0f, 0x00, 0xe7, 0xe7, 0x3a, 0xa3, 0xde, - 0x39, 0xd2, 0xa5, 0xf6, 0x99, 0x45, 0xfa, 0x47, 0xfd, 0xae, 0x29, 0x77, 0x47, 0x25, 0xc9, 0x94, - 0xda, 0x96, 0x49, 0x06, 0x7d, 0xc9, 0x55, 0x9b, 0xa0, 0xb2, 0xf4, 0xd1, 0xf1, 0x69, 0x67, 0xdc, - 0x25, 0xfd, 0x8e, 0x85, 0x2a, 0xd2, 0x47, 0x4f, 0x87, 0x4b, 0xa0, 0x8a, 0xef, 0xc3, 0xd6, 0x0a, - 0xe0, 0x98, 0x83, 0x01, 0xaa, 0xed, 0xfd, 0x41, 0x83, 0xcd, 0x95, 0xd2, 0x4a, 0x91, 0xe1, 0xc8, - 0x49, 0x91, 0xf4, 0x64, 0xe9, 0x19, 0xd2, 0xb8, 0x90, 0x86, 0x31, 0x34, 0x53, 0x64, 0xbe, 0x3f, - 0x2a, 0x60, 0x80, 0x32, 0xb1, 0xc6, 0xa7, 0x03, 0x1b, 0x15, 0xf1, 0x3d, 0x68, 0x2c, 0xd2, 0xe9, - 0x98, 0xe4, 0x29, 0xd2, 0xa5, 0xe1, 0xf7, 0x7b, 0xd6, 0xd0, 0xee, 0x1f, 0xf5, 0x2d, 0x82, 0x4a, - 0x92, 0xd2, 0xb3, 0x8e, 0xcc, 0xd3, 0x81, 0xed, 0x9c, 0x99, 0x83, 0x53, 0x0b, 0x95, 0x25, 0x25, - 0x55, 0x7d, 0x66, 0x8e, 0x9f, 0xa1, 0xca, 0xde, 0xef, 0x96, 0x61, 0xc9, 0xbc, 0xe3, 0x1a, 0x94, - 0xac, 0xe3, 0x13, 0xfb, 0x3c, 0x0d, 0x49, 0xcd, 0xc8, 0xb4, 0x4a, 0x7d, 0x4d, 0x1e, 0x2c, 0x45, - 0xba, 0xe6, 0x70, 0x34, 0xec, 0x77, 0xcd, 0x01, 0x2a, 0xc8, 0x0a, 0xa4, 0x60, 0xaf, 0xaf, 0xca, - 0x66, 0x92, 0x73, 0x54, 0xc4, 0x6d, 0xf8, 0xde, 0xbb, 0xa8, 0x33, 0x22, 0xce, 0x88, 0xf4, 0x2c, - 0x62, 0xf5, 0x90, 0x2e, 0xcb, 0x9e, 0xc5, 0x86, 0xca, 0x1d, 0xeb, 0x4f, 0xaf, 0x76, 0xb4, 0x6f, - 0x5e, 0xed, 0x68, 0xdf, 0xbe, 0xda, 0xd1, 0xfe, 0xf1, 0x6a, 0x47, 0xfb, 0xfd, 0xeb, 0x9d, 0x8d, - 0x6f, 0x5f, 0xef, 0x6c, 0xfc, 0xfd, 0xf5, 0xce, 0xc6, 0x6f, 0xbe, 0x98, 0x72, 0x71, 0x95, 0x5c, - 0xec, 0xbb, 0xc1, 0xec, 0x20, 0xf7, 0x97, 0xea, 0xfa, 0xc9, 0xc1, 0xcb, 0xf4, 0x7f, 0x95, 0xbc, - 0x53, 0xf1, 0x45, 0x59, 0xfd, 0x4d, 0x7a, 0xf2, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfb, 0x22, - 0x4b, 0xc8, 0x79, 0x0d, 0x00, 0x00, + 0x9f, 0xfb, 0x0f, 0xf4, 0x50, 0xa0, 0xff, 0x42, 0x81, 0x05, 0x0a, 0xf4, 0xbf, 0xd8, 0xe3, 0xde, + 0xda, 0x93, 0x51, 0x24, 0x87, 0x02, 0x39, 0xe6, 0x52, 0xf4, 0x50, 0xa0, 0x98, 0x21, 0xf5, 0x83, + 0x89, 0xe2, 0x62, 0xf7, 0x24, 0xcd, 0xf7, 0xbe, 0xf9, 0xe6, 0xcd, 0x7b, 0x33, 0xdf, 0x48, 0xf0, + 0x63, 0x8f, 0x5e, 0x53, 0x9f, 0x89, 0x03, 0xf9, 0x79, 0x10, 0x87, 0xcc, 0x3d, 0xa0, 0x21, 0x77, + 0xdc, 0xc0, 0xf3, 0x98, 0x2b, 0x78, 0xe0, 0xef, 0x87, 0x51, 0x20, 0x02, 0x7c, 0x2f, 0xe3, 0xed, + 0xcb, 0xcf, 0x7d, 0xc9, 0x7b, 0xb8, 0x3d, 0x0d, 0xa6, 0x81, 0x8a, 0x1e, 0xc8, 0x6f, 0x29, 0xd1, + 0xf8, 0x6f, 0x11, 0x1a, 0x66, 0xc8, 0xbb, 0x0b, 0x01, 0xdc, 0x82, 0x0a, 0xf3, 0xe9, 0x85, 0xc7, + 0x26, 0x2d, 0x6d, 0x57, 0x7b, 0x5c, 0x25, 0xf3, 0x21, 0x3e, 0x81, 0xad, 0xe5, 0x42, 0xce, 0x84, + 0x0a, 0xda, 0x2a, 0xec, 0x6a, 0x8f, 0x37, 0x0f, 0x7f, 0xb8, 0xff, 0xde, 0x72, 0xfb, 0x4b, 0xc5, + 0x1e, 0x15, 0xb4, 0xa3, 0x7f, 0x7d, 0xdb, 0xde, 0x20, 0x4d, 0x37, 0x87, 0xe2, 0x3d, 0xd0, 0x69, + 0xc8, 0xe3, 0x56, 0x71, 0xb7, 0xf8, 0x78, 0xf3, 0xf0, 0xc1, 0x1a, 0x19, 0x33, 0xe4, 0x44, 0x71, + 0xf0, 0x13, 0xa8, 0x5c, 0x31, 0x3a, 0x61, 0x51, 0xdc, 0xd2, 0x15, 0xfd, 0xa3, 0x35, 0xf4, 0x67, + 0x8a, 0x41, 0xe6, 0x4c, 0x3c, 0x00, 0xc4, 0xfd, 0x2b, 0x16, 0x71, 0x41, 0x7d, 0x97, 0x39, 0x6a, + 0xb1, 0x92, 0x9a, 0xfd, 0xff, 0x73, 0x26, 0x5b, 0x2b, 0x53, 0x4d, 0x99, 0xc2, 0x00, 0x50, 0x48, + 0xa3, 0x98, 0x39, 0x13, 0x1e, 0x49, 0xde, 0x35, 0x8b, 0x5b, 0xe5, 0x0f, 0xaa, 0x9d, 0x48, 0x6a, + 0x6f, 0xce, 0x24, 0x5b, 0x61, 0x6e, 0x1c, 0xe3, 0x5f, 0x00, 0xb0, 0x97, 0x82, 0xf9, 0x31, 0x0f, + 0xfc, 0xb8, 0x55, 0x51, 0x3a, 0x8f, 0xd6, 0xe8, 0x58, 0x73, 0x12, 0x59, 0xe1, 0x63, 0x0b, 0x1a, + 0xd7, 0x2c, 0xe2, 0x97, 0xdc, 0xa5, 0x42, 0x09, 0x54, 0x95, 0x40, 0x7b, 0x8d, 0xc0, 0xd9, 0x0a, + 0x8f, 0xe4, 0x67, 0x19, 0xbf, 0x87, 0xda, 0x42, 0x1f, 0x63, 0xd0, 0x7d, 0x3a, 0x63, 0xaa, 0xef, + 0x35, 0xa2, 0xbe, 0xe3, 0xcf, 0x41, 0x8f, 0x12, 0x8f, 0xb5, 0x8a, 0xaa, 0xd3, 0xdf, 0x5f, 0x23, + 0x4f, 0x12, 0x8f, 0x11, 0x45, 0xc2, 0x9f, 0x40, 0xc3, 0x4d, 0x9c, 0x59, 0xe2, 0x09, 0x1e, 0x7a, + 0x9c, 0x45, 0x2d, 0x7d, 0x57, 0x7b, 0xac, 0x93, 0xba, 0x9b, 0x1c, 0x2f, 0xb0, 0xe7, 0x7a, 0xb5, + 0x80, 0x8a, 0xc6, 0x23, 0xd0, 0xe5, 0x44, 0xbc, 0x0d, 0xa5, 0x0b, 0x2f, 0x70, 0xbf, 0x54, 0x8b, + 0xea, 0x24, 0x1d, 0x18, 0x5f, 0x69, 0x50, 0x5f, 0x4d, 0x7b, 0x6d, 0x6a, 0xcf, 0x61, 0xeb, 0x9d, + 0x76, 0xdc, 0x71, 0x1e, 0xdf, 0xe9, 0x46, 0x33, 0xdf, 0x0d, 0xfc, 0x33, 0x28, 0x5f, 0x53, 0x2f, + 0x61, 0xf3, 0xb3, 0xf8, 0xf1, 0x87, 0x24, 0xce, 0x24, 0x8b, 0x64, 0xe4, 0xe7, 0x7a, 0x55, 0x47, + 0x25, 0xe3, 0x3f, 0x1a, 0xc0, 0x32, 0x88, 0x1f, 0x41, 0x6d, 0xd1, 0xa8, 0x2c, 0xe1, 0x25, 0x80, + 0x7f, 0x04, 0x4d, 0xf6, 0x32, 0x64, 0xae, 0x60, 0x13, 0x47, 0xa9, 0xa8, 0xa4, 0x6b, 0xa4, 0x31, + 0x47, 0x53, 0x91, 0xcf, 0x60, 0xcb, 0xa3, 0x82, 0xc5, 0xc2, 0x99, 0xf0, 0x58, 0x1d, 0x41, 0xd5, + 0x02, 0x9d, 0x34, 0x53, 0xb8, 0x97, 0xa1, 0x78, 0x08, 0xd5, 0x98, 0xc9, 0xa6, 0x8a, 0x1b, 0x55, + 0xee, 0xe6, 0xe1, 0xe1, 0x9d, 0xb9, 0xe7, 0x8e, 0xc3, 0x38, 0x9b, 0x49, 0x16, 0x1a, 0xc6, 0x4f, + 0x60, 0x7b, 0x1d, 0x03, 0x57, 0x41, 0x3f, 0xa2, 0xdc, 0x43, 0x1b, 0x78, 0x13, 0x2a, 0xbf, 0xa1, + 0x91, 0xcf, 0xfd, 0x29, 0xd2, 0x8c, 0xbf, 0x15, 0xa0, 0x99, 0xbf, 0x37, 0xf8, 0x0c, 0x1a, 0xd2, + 0x94, 0xb8, 0x2f, 0x58, 0x74, 0x49, 0xdd, 0xac, 0x69, 0x9d, 0x9f, 0xbe, 0xb9, 0x6d, 0xe7, 0x03, + 0x6f, 0x6f, 0xdb, 0x8f, 0x66, 0x34, 0x8c, 0x45, 0x94, 0xb8, 0x22, 0x89, 0xd8, 0x17, 0x46, 0x2e, + 0x6c, 0x90, 0x3a, 0x0d, 0x79, 0x7f, 0x3e, 0x94, 0xba, 0x2a, 0xe6, 0x53, 0xcf, 0x09, 0xa9, 0xb8, + 0x4a, 0x0b, 0x97, 0xea, 0xe6, 0x02, 0xef, 0xeb, 0xe6, 0xc2, 0x06, 0xa9, 0xcf, 0xc7, 0x27, 0x54, + 0x5c, 0xe1, 0x27, 0xa0, 0x8b, 0x9b, 0x30, 0xad, 0x6f, 0xad, 0xd3, 0x7e, 0x73, 0xdb, 0x56, 0xe3, + 0xb7, 0xb7, 0xed, 0xfb, 0x79, 0x15, 0x89, 0x1a, 0x44, 0x05, 0xf1, 0x17, 0x50, 0xa6, 0x93, 0x89, + 0x13, 0xf8, 0xaa, 0xe8, 0xb5, 0xce, 0x27, 0x6f, 0x6e, 0xdb, 0x19, 0xf2, 0xf6, 0xb6, 0xfd, 0xbd, + 0x77, 0xb6, 0xa5, 0x70, 0x83, 0x94, 0xe8, 0x64, 0x32, 0xf2, 0x8d, 0x7f, 0x69, 0x50, 0x4e, 0x9d, + 0x6a, 0xed, 0xb9, 0xfe, 0x39, 0xe8, 0x5f, 0x72, 0x7f, 0xa2, 0xb6, 0xd7, 0x3c, 0xfc, 0xf4, 0x83, + 0x36, 0x97, 0x7d, 0xd8, 0x37, 0x21, 0x23, 0x6a, 0x06, 0xee, 0x40, 0xfd, 0x32, 0xf1, 0x53, 0x7f, + 0x16, 0x74, 0xaa, 0x76, 0xd4, 0x5c, 0xeb, 0x09, 0x47, 0xa7, 0xc3, 0xae, 0xdd, 0x1f, 0x0d, 0x1d, + 0xdb, 0x7c, 0x4a, 0x36, 0xe7, 0x93, 0x6c, 0x3a, 0x35, 0x5e, 0x00, 0x2c, 0x75, 0x71, 0x03, 0x6a, + 0x21, 0x8d, 0x63, 0x27, 0x66, 0xfe, 0x04, 0x6d, 0xe0, 0x26, 0x80, 0x1a, 0x46, 0x2c, 0xf4, 0x6e, + 0x90, 0xb6, 0x08, 0x5f, 0x04, 0xe2, 0x0a, 0x15, 0xf0, 0x16, 0x6c, 0xaa, 0x21, 0x9f, 0xfa, 0x41, + 0xc4, 0x50, 0xd1, 0xf8, 0x77, 0x01, 0x8a, 0x66, 0xc8, 0xef, 0x78, 0x54, 0xe6, 0x05, 0x28, 0xac, + 0x14, 0x40, 0xda, 0x48, 0x30, 0x0b, 0x13, 0xc1, 0x9c, 0xc4, 0xe7, 0x22, 0xce, 0x4e, 0x7e, 0x3d, + 0x03, 0x4f, 0x25, 0x86, 0xf7, 0xe1, 0x3e, 0x7b, 0x29, 0x22, 0xea, 0xe4, 0xa9, 0xa9, 0xe3, 0xdc, + 0x53, 0xa1, 0xee, 0x2a, 0xdf, 0x84, 0xaa, 0x4b, 0x05, 0x9b, 0x06, 0xd1, 0x4d, 0xab, 0xac, 0x6c, + 0x62, 0x5d, 0x5d, 0xc6, 0x21, 0x73, 0xbb, 0x19, 0x2d, 0x7b, 0xb4, 0x16, 0xd3, 0x70, 0x1f, 0x1a, + 0xca, 0x9e, 0x1c, 0x69, 0x1e, 0xdc, 0x9f, 0xb6, 0x2a, 0x4a, 0x67, 0x67, 0x8d, 0x4e, 0x47, 0xf2, + 0xd4, 0xa5, 0x8b, 0x32, 0x99, 0xfa, 0xc5, 0x1c, 0xe2, 0xfe, 0x14, 0x7f, 0x0c, 0x20, 0xf8, 0x8c, + 0x05, 0x89, 0x70, 0x66, 0xd2, 0xbb, 0x65, 0xd2, 0xb5, 0x0c, 0x39, 0x8e, 0xf1, 0xaf, 0xa1, 0xa2, + 0x0c, 0x2a, 0x8a, 0x5b, 0x35, 0xe5, 0x47, 0xbb, 0x6b, 0xd6, 0x78, 0xca, 0x7c, 0x16, 0x71, 0x37, + 0xb7, 0xca, 0x7c, 0x9a, 0xf1, 0x55, 0x01, 0x9a, 0x79, 0xcf, 0x7b, 0xef, 0x74, 0x68, 0xdf, 0xfe, + 0x74, 0xe0, 0xcf, 0xe1, 0xde, 0x52, 0x83, 0xcd, 0x42, 0x69, 0x46, 0x59, 0xef, 0xd0, 0x82, 0x97, + 0xe1, 0xf8, 0x05, 0x34, 0x23, 0x16, 0x27, 0x9e, 0x58, 0x14, 0xac, 0xf8, 0x2d, 0x0a, 0xd6, 0x48, + 0xe7, 0xce, 0x2b, 0xf6, 0x11, 0x54, 0xa5, 0x3b, 0xa8, 0xc3, 0xa2, 0xae, 0x1c, 0xa9, 0xd0, 0x90, + 0x0f, 0xe5, 0x79, 0x59, 0xa9, 0x56, 0xe9, 0xbb, 0x55, 0xeb, 0xaf, 0x1a, 0x6c, 0xae, 0x64, 0x20, + 0xdb, 0x93, 0x86, 0x1c, 0x1a, 0xc9, 0x42, 0x15, 0xa5, 0x87, 0xa7, 0x88, 0x19, 0x4d, 0xf1, 0xaf, + 0xe4, 0x39, 0x57, 0x61, 0xb9, 0xe7, 0xec, 0xa2, 0xae, 0xdb, 0xd5, 0x89, 0x49, 0xc6, 0x16, 0x71, + 0x64, 0x3d, 0x49, 0xa6, 0x78, 0x94, 0xf8, 0xae, 0x3c, 0xe1, 0x13, 0x76, 0x49, 0x65, 0x69, 0xd2, + 0x37, 0x40, 0x79, 0x0f, 0xa9, 0x67, 0x60, 0xfa, 0x04, 0x3c, 0x84, 0x2a, 0xf3, 0xdd, 0x60, 0x22, + 0x0b, 0x97, 0xee, 0x78, 0x31, 0x36, 0xfe, 0xac, 0x41, 0x23, 0xb7, 0xa3, 0x45, 0xca, 0xa9, 0x35, + 0x66, 0xcf, 0x8e, 0x42, 0x94, 0xc9, 0x6d, 0x43, 0x69, 0xf5, 0xb5, 0x49, 0x07, 0xf8, 0x97, 0xf3, + 0x49, 0x0b, 0x03, 0xbc, 0x73, 0x1f, 0xf6, 0xf9, 0x89, 0x95, 0x89, 0x2a, 0x77, 0xc0, 0xd9, 0x8f, + 0x83, 0x34, 0x3b, 0xf5, 0x5d, 0x3d, 0xdd, 0xab, 0xb7, 0x08, 0x7f, 0x2a, 0xf7, 0x2a, 0x58, 0x34, + 0xe3, 0x3e, 0x8f, 0x05, 0x77, 0x33, 0x07, 0xc8, 0x83, 0x32, 0x3f, 0x2f, 0x70, 0xa9, 0xa7, 0xf2, + 0xab, 0x92, 0x74, 0x80, 0x0d, 0xa8, 0xc7, 0xc9, 0x45, 0xec, 0x46, 0x3c, 0x94, 0x27, 0x4b, 0x65, + 0x58, 0x25, 0x39, 0x4c, 0x96, 0x29, 0x16, 0x54, 0xb0, 0xcb, 0xc4, 0x53, 0x89, 0x34, 0xc8, 0x62, + 0x8c, 0xdb, 0xb0, 0x79, 0x45, 0xfd, 0x29, 0xf7, 0xa7, 0xf2, 0xb7, 0x5f, 0xab, 0xa4, 0xa6, 0x43, + 0x06, 0x99, 0x21, 0xdf, 0x33, 0xa0, 0x66, 0xfd, 0xd6, 0xb6, 0x86, 0xe3, 0xfe, 0x68, 0x28, 0x9f, + 0xb8, 0xe1, 0x68, 0x68, 0xa5, 0x4f, 0x9c, 0x49, 0xba, 0xcf, 0xfa, 0x67, 0x16, 0xd2, 0xf6, 0xfe, + 0xae, 0x41, 0x7d, 0xf5, 0x46, 0xe0, 0x3a, 0x54, 0x7b, 0xfd, 0xb1, 0xd9, 0x19, 0x58, 0x3d, 0xb4, + 0x81, 0x11, 0xd4, 0x9f, 0x5a, 0xb6, 0xd3, 0x19, 0x8c, 0xba, 0x2f, 0x86, 0xa7, 0xc7, 0x48, 0xc3, + 0xdb, 0x80, 0x16, 0x88, 0xd3, 0x39, 0x77, 0x24, 0x5a, 0xc0, 0x0f, 0xe1, 0xc1, 0xd8, 0xb2, 0x9d, + 0x81, 0x69, 0x5b, 0x63, 0xdb, 0xe9, 0x0f, 0x9d, 0x63, 0xcb, 0x36, 0x7b, 0xa6, 0x6d, 0xa2, 0x22, + 0x7e, 0x00, 0x38, 0x1f, 0xeb, 0x8c, 0x7a, 0xe7, 0x48, 0x97, 0xda, 0x67, 0x16, 0xe9, 0x1f, 0xf5, + 0xbb, 0xa6, 0x5c, 0x1d, 0x95, 0x24, 0x53, 0x6a, 0x5b, 0x26, 0x19, 0xf4, 0x25, 0x57, 0x2d, 0x82, + 0xca, 0xd2, 0x89, 0xc7, 0xa7, 0x9d, 0x71, 0x97, 0xf4, 0x3b, 0x16, 0xaa, 0x48, 0x27, 0x3e, 0x1d, + 0x2e, 0x81, 0x2a, 0xbe, 0x0f, 0x5b, 0x2b, 0x80, 0x63, 0x0e, 0x06, 0xa8, 0xb6, 0xf7, 0x27, 0x0d, + 0x36, 0x57, 0x5a, 0x2b, 0x45, 0x86, 0x23, 0x27, 0x45, 0xd2, 0x9d, 0xa5, 0x7b, 0x48, 0xf3, 0x42, + 0x1a, 0xc6, 0xd0, 0x4c, 0x91, 0xf9, 0xfa, 0xa8, 0x80, 0x01, 0xca, 0xc4, 0x1a, 0x9f, 0x0e, 0x6c, + 0x54, 0xc4, 0xf7, 0xa0, 0xb1, 0x28, 0xa7, 0x63, 0x92, 0xa7, 0x48, 0x97, 0x4f, 0x46, 0xbf, 0x67, + 0x0d, 0xed, 0xfe, 0x51, 0xdf, 0x22, 0xa8, 0x24, 0x29, 0x3d, 0xeb, 0xc8, 0x3c, 0x1d, 0xd8, 0xce, + 0x99, 0x39, 0x38, 0xb5, 0x50, 0x59, 0x52, 0x52, 0xd5, 0x67, 0xe6, 0xf8, 0x19, 0xaa, 0xec, 0xfd, + 0x61, 0x99, 0x96, 0xac, 0x3b, 0xae, 0x41, 0xc9, 0x3a, 0x3e, 0xb1, 0xcf, 0xd3, 0x94, 0x54, 0x44, + 0x96, 0x55, 0xea, 0x6b, 0x72, 0x63, 0x29, 0xd2, 0x35, 0x87, 0xa3, 0x61, 0xbf, 0x6b, 0x0e, 0x50, + 0x41, 0x76, 0x20, 0x05, 0x7b, 0x7d, 0xd5, 0x36, 0x93, 0x9c, 0xa3, 0x22, 0x6e, 0xc3, 0x0f, 0xde, + 0x45, 0x9d, 0x11, 0x71, 0x46, 0xa4, 0x67, 0x11, 0xab, 0x87, 0x74, 0xd9, 0xf6, 0x2c, 0x37, 0x54, + 0xee, 0x58, 0x7f, 0x79, 0xb5, 0xa3, 0x7d, 0xfd, 0x6a, 0x47, 0xfb, 0xe6, 0xd5, 0x8e, 0xf6, 0xcf, + 0x57, 0x3b, 0xda, 0x1f, 0x5f, 0xef, 0x6c, 0x7c, 0xf3, 0x7a, 0x67, 0xe3, 0x1f, 0xaf, 0x77, 0x36, + 0x7e, 0xf7, 0xd9, 0x94, 0x8b, 0xab, 0xe4, 0x62, 0xdf, 0x0d, 0x66, 0x07, 0xb9, 0x3f, 0x65, 0xd7, + 0x4f, 0x0e, 0x5e, 0xa6, 0xff, 0xcc, 0xe4, 0x9d, 0x8a, 0x2f, 0xca, 0xea, 0x8f, 0xd6, 0x93, 0xff, + 0x05, 0x00, 0x00, 0xff, 0xff, 0xe6, 0xb2, 0x04, 0x99, 0xbb, 0x0d, 0x00, 0x00, } func (this *ApiCollection) Equal(that interface{}) bool { @@ -1545,6 +1553,14 @@ func (this *ParseDirective) Equal(that interface{}) bool { if this.ApiName != that1.ApiName { return false } + if len(this.Parsers) != len(that1.Parsers) { + return false + } + for i := range this.Parsers { + if !this.Parsers[i].Equal(&that1.Parsers[i]) { + return false + } + } return true } func (this *BlockParser) Equal(that interface{}) bool { @@ -2159,6 +2175,20 @@ func (m *ParseDirective) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Parsers) > 0 { + for iNdEx := len(m.Parsers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Parsers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApiCollection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } if len(m.ApiName) > 0 { i -= len(m.ApiName) copy(dAtA[i:], m.ApiName) @@ -2596,6 +2626,12 @@ func (m *ParseDirective) Size() (n int) { if l > 0 { n += 1 + l + sovApiCollection(uint64(l)) } + if len(m.Parsers) > 0 { + for _, e := range m.Parsers { + l = e.Size() + n += 1 + l + sovApiCollection(uint64(l)) + } + } return n } @@ -4198,6 +4234,40 @@ func (m *ParseDirective) Unmarshal(dAtA []byte) error { } m.ApiName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Parsers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApiCollection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApiCollection + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApiCollection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Parsers = append(m.Parsers, GenericParser{}) + if err := m.Parsers[len(m.Parsers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApiCollection(dAtA[iNdEx:]) diff --git a/x/spec/types/combinable.go b/x/spec/types/combinable.go index f0c9b781a5..2ee3c5a7b8 100644 --- a/x/spec/types/combinable.go +++ b/x/spec/types/combinable.go @@ -141,7 +141,7 @@ func CombineUnique[T Combinable](appendFrom, appendTo []T, currentMap map[string } else { // overwriting the inherited field might need Overwrite actions if overwritten, isOverwritten := current.currentCombinable.Overwrite(combinable); isOverwritten { - if appendTo[current.index].Differeniator() != combinable.Differeniator() { + if len(appendTo) <= current.index || appendTo[current.index].Differeniator() != combinable.Differeniator() { return nil, fmt.Errorf("differentiator mismatch in overwrite %s vs %s", combinable.Differeniator(), appendTo[current.index].Differeniator()) } overwrittenT, ok := overwritten.(T) diff --git a/x/spec/types/spec.go b/x/spec/types/spec.go index 237feb9bf4..f41ee8fda0 100644 --- a/x/spec/types/spec.go +++ b/x/spec/types/spec.go @@ -200,6 +200,9 @@ func (spec Spec) ValidateSpec(maxCU uint64) (map[string]string, error) { } func (spec *Spec) CombineCollections(parentsCollections map[CollectionData][]*ApiCollection) error { + if spec == nil { + return fmt.Errorf("CombineCollections: spec is nil") + } collectionDataList := make([]CollectionData, 0) // Populate the keys slice with the map keys for key := range parentsCollections { @@ -225,7 +228,7 @@ func (spec *Spec) CombineCollections(parentsCollections map[CollectionData][]*Ap break } } - if !combined.Enabled { + if combined == nil || !combined.Enabled { // no collections enabled to combine, we skip this continue } diff --git a/x/subscription/keeper/cu_tracker.go b/x/subscription/keeper/cu_tracker.go index a742913b99..6426cffc5b 100644 --- a/x/subscription/keeper/cu_tracker.go +++ b/x/subscription/keeper/cu_tracker.go @@ -195,7 +195,7 @@ func (k Keeper) RewardAndResetCuTracker(ctx sdk.Context, cuTrackerTimerKeyBytes // Note: if the reward function doesn't reward the provider // because he was unstaked, we only print an error and not returning - _, _, err := k.dualstakingKeeper.RewardProvidersAndDelegators(ctx, provider, chainID, sdk.NewCoins(creditToSub), types.ModuleName, false, false, false) + _, err := k.dualstakingKeeper.RewardProvidersAndDelegators(ctx, provider, chainID, sdk.NewCoins(creditToSub), types.ModuleName, false, false, false) if errors.Is(err, epochstoragetypes.ErrProviderNotStaked) || errors.Is(err, epochstoragetypes.ErrStakeStorageNotFound) { utils.LavaFormatWarning("sending provider reward with delegations failed", err, utils.Attribute{Key: "provider", Value: provider}, diff --git a/x/subscription/keeper/grpc_query_estimated_rewards.go b/x/subscription/keeper/grpc_query_estimated_rewards.go index 6c180b44ef..b8b6771858 100644 --- a/x/subscription/keeper/grpc_query_estimated_rewards.go +++ b/x/subscription/keeper/grpc_query_estimated_rewards.go @@ -45,7 +45,7 @@ func (k Keeper) EstimatedRewards(goCtx context.Context, req *types.QueryEstimate if req.AmountDelegator != "" { _, err := sdk.AccAddressFromBech32(req.AmountDelegator) if err == nil { - d, found := k.dualstakingKeeper.GetDelegation(ctx, req.AmountDelegator, req.Provider, req.ChainId, uint64(ctx.BlockHeight())) + d, found := k.dualstakingKeeper.GetDelegation(ctx, req.AmountDelegator, req.Provider) if !found { return nil, fmt.Errorf("could not find delegator") } diff --git a/x/subscription/keeper/subscription.go b/x/subscription/keeper/subscription.go index 7a8a04d3f9..7cde34e7ed 100644 --- a/x/subscription/keeper/subscription.go +++ b/x/subscription/keeper/subscription.go @@ -192,6 +192,9 @@ func (k Keeper) verifySubscriptionBuyInputAndGetPlan(ctx sdk.Context, block uint func (k Keeper) createNewSubscription(ctx sdk.Context, plan *planstypes.Plan, creator, consumer string, block uint64, autoRenewalFlag bool, ) (types.Subscription, error) { + if plan == nil { + return types.Subscription{}, utils.LavaFormatError("plan is nil", fmt.Errorf("createNewSubscription: cannot create new subscription")) + } autoRenewalNextPlan := types.AUTO_RENEWAL_PLAN_NONE if autoRenewalFlag { // On subscription creation, auto renewal is set to the subscription's plan @@ -223,6 +226,13 @@ func (k Keeper) createNewSubscription(ctx sdk.Context, plan *planstypes.Plan, cr } func (k Keeper) upgradeSubscriptionPlan(ctx sdk.Context, sub *types.Subscription, newPlan *planstypes.Plan) error { + if newPlan == nil { + return utils.LavaFormatError("new plan is nil", fmt.Errorf("upgradeSubscriptionPlan: cannot upgrade subscription plan")) + } + if sub == nil { + return utils.LavaFormatError("subscription is nil", fmt.Errorf("upgradeSubscriptionPlan: cannot upgrade subscription plan")) + } + block := uint64(ctx.BlockHeight()) nextEpoch, err := k.epochstorageKeeper.GetNextEpoch(ctx, block) diff --git a/x/subscription/types/expected_keepers.go b/x/subscription/types/expected_keepers.go index 264fe6f6d1..a1c4b0525f 100644 --- a/x/subscription/types/expected_keepers.go +++ b/x/subscription/types/expected_keepers.go @@ -68,8 +68,8 @@ type TimerStoreKeeper interface { } type DualStakingKeeper interface { - RewardProvidersAndDelegators(ctx sdk.Context, providerAddr string, chainID string, totalReward sdk.Coins, senderModule string, calcOnlyProvider bool, calcOnlyDelegators bool, calcOnlyContributor bool) (providerReward sdk.Coins, totalRewards sdk.Coins, err error) - GetDelegation(ctx sdk.Context, delegator, provider, chainID string, epoch uint64) (dualstakingtypes.Delegation, bool) + RewardProvidersAndDelegators(ctx sdk.Context, providerAddr string, chainID string, totalReward sdk.Coins, senderModule string, calcOnlyProvider bool, calcOnlyDelegators bool, calcOnlyContributor bool) (providerReward sdk.Coins, err error) + GetDelegation(ctx sdk.Context, delegator, provider string) (dualstakingtypes.Delegation, bool) } type RewardsKeeper interface {