diff --git a/app/provider/app.go b/app/provider/app.go index 40e11d1..3ccd3aa 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -392,12 +392,14 @@ func New( app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, + app.DistrKeeper, govkeeper.Keeper{}, // will be set after the GovKeeper is created authtypes.NewModuleAddress(govtypes.ModuleName).String(), authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), authtypes.FeeCollectorName, ) + app.BankKeeper.AppendSendRestriction(app.ProviderKeeper.FeePoolSendRestriction()) govConfig := govtypes.DefaultConfig() app.GovKeeper = govkeeper.NewKeeper( diff --git a/docs/consumer-fee-pool.md b/docs/consumer-fee-pool.md new file mode 100644 index 0000000..d15d85c --- /dev/null +++ b/docs/consumer-fee-pool.md @@ -0,0 +1,120 @@ +# Consumer Fee Pool + +Every consumer chain on VAAS has a dedicated fee pool on the provider chain, +held at a deterministic account address derived from the consumer ID: + + fee_pool_address = NewModuleAddress("vaas-consumer-fee-pool-") + +This account funds the per-block service charge that the provider drains +from the pool every block (`fees_per_block`) while the consumer is in +`CONSUMER_PHASE_LAUNCHED`. If the pool is short, the consumer is flagged as +in-debt and its ante gate blocks user transactions until funding is restored. + +## Funding + +Funding the pool MUST go through `MsgFundConsumerFeePool`. Direct bank sends +to the fee pool address are rejected by a `bank.SendRestriction` registered +on the provider chain — funds sent that way will either bounce (IBC) or fail +the transaction (direct `MsgSend`). This restriction exists so the +share-accounting (see below) never gets out of sync with the actual pool +balance. + +`MsgFundConsumerFeePool` accepts a single `Coin` whose denom must match the +current `fees_per_block.Denom`. Anyone may sign. The signer is credited with +shares. + +### Cross-chain funding via ICA + +To fund a pool from another chain, register an Interchain Account on the +provider, IBC-transfer funds into the ICA's account, and have the controller +side send a `MsgFundConsumerFeePool` from the ICA. The ICA becomes the +depositor of record. + +A direct IBC transfer addressed to a fee pool fails losslessly: the bank +send-restriction rejects the receive on the provider, the packet acks with an +error, and the source-chain transfer module refunds the sender via standard +IBC semantics. The funds are not lost, just not deposited. + +### Funding from the community pool + +A governance proposal containing `MsgFundConsumerFeePool` with the gov +module authority as `signer` will pull funds from the cosmos-sdk +distribution community pool and credit the distribution module account as +the depositor. + +## Withdrawing + +Each depositor controls their own shares and can withdraw at any time via +`MsgWithdrawConsumerFeePool`. The message accepts multi-denom `Coins` and +is atomic — if any denom in the request fails its share check, the whole +transaction reverts. + +### Share math (TL;DR) + +- Shares are minted when you deposit. Initial deposit mints + `shares = amount`; subsequent deposits mint + `amount × total_shares / pool_balance` (balance BEFORE this deposit). +- Your claim at any time is + `your_shares × pool_balance / total_shares`. +- A withdraw of `amount ≥ claim` burns all your shares and delivers your + exact claim. Partial withdraws (`amount < claim`) burn proportional + shares and may deliver marginally less than requested due to integer + truncation. + +This is the same accounting pattern used by ERC-4626 vaults and liquid +staking modules: per-block fee consumption reduces share value, not share +count, so consumption is borne pro-rata by current share-holders. + +## Sweeping + +The consumer owner can trigger a full settlement via +`MsgSweepConsumerFeePool`, distributing the pool pro-rata to all +share-holders. The message takes an optional list of denoms; if empty, all +denoms with shares or balance are swept. Any truncation residue per denom +is forwarded to the community pool. + +The same sweep runs automatically when a consumer is deleted (auto-sweep +on `DeleteConsumerChain`). If the auto-sweep fails for any reason, the +delete aborts and the consumer stays in `STOPPED` — funds are never +silently lost. + +## Trust model + +- Producer governance has **no** unilateral authority over consumer-owned + funds. Gov interacts as a single depositor (via the community pool path) + using the same messages as everyone else. +- The consumer owner can trigger settlement but cannot redirect funds to + arbitrary recipients — pro-rata distribution to known depositors is the + only outcome. +- Each depositor controls their own shares independently. + +## Queries + +- `appd query provider consumer-fee-pool-claim ` + -- one depositor's claim across all denoms. Pass the gov authority address + to query the community pool's holdings (the query aliases the gov authority + to the distribution module account, which is the depositor of record for + community-pool funding). +- `appd query provider consumer-fee-pool-claims ` -- + paginated list of all depositors with non-zero claims. + +## CLI examples + + # fund a pool with 1000uphoton from your key + appd tx provider fund-consumer-fee-pool 5 1000uphoton --from operator + + # withdraw a mix of denoms from your share in pool 5 + appd tx provider withdraw-consumer-fee-pool 5 250uphoton,30uatone --from operator + + # owner sweeps all denoms with shares or balance + appd tx provider sweep-consumer-fee-pool 5 --from owner + + # owner sweeps only the listed denoms (comma-separated or repeated flag) + appd tx provider sweep-consumer-fee-pool 5 --denoms=uphoton,uatone --from owner + appd tx provider sweep-consumer-fee-pool 5 --denoms=uphoton --denoms=uatone --from owner + + # query a single depositor's claim + appd query provider consumer-fee-pool-claim 5 cosmos1... + + # paginated list of all depositors with non-zero claims + appd query provider consumer-fee-pool-claims 5 --page 1 --limit 100 diff --git a/proto/vaas/provider/v1/genesis.proto b/proto/vaas/provider/v1/genesis.proto index 366faa0..7aed386 100644 --- a/proto/vaas/provider/v1/genesis.proto +++ b/proto/vaas/provider/v1/genesis.proto @@ -6,6 +6,7 @@ option go_package = "github.com/allinbits/vaas/x/vaas/provider/types"; import "gogoproto/gogo.proto"; import "google/protobuf/timestamp.proto"; +import "cosmos_proto/cosmos.proto"; import "vaas/v1/shared_consumer.proto"; import "vaas/v1/wire.proto"; import "vaas/provider/v1/provider.proto"; @@ -33,6 +34,9 @@ message GenesisState { // empty for a new chain repeated ConsumerAddrsToPrune consumer_addrs_to_prune = 7 [ (gogoproto.nullable) = false ]; + // empty for a new chain + repeated ConsumerFeePoolShare consumer_fee_pool_shares = 8 + [ (gogoproto.nullable) = false ]; } // The provider VAAS module's knowledge of consumer state. @@ -84,3 +88,17 @@ message ValsetUpdateIdToHeight { uint64 valset_update_id = 1; uint64 height = 2; } + +// ConsumerFeePoolShare is a single depositor's share holding in a consumer +// fee pool, scoped to one denom. The triple (consumer_id, depositor, denom) +// is unique. +message ConsumerFeePoolShare { + uint64 consumer_id = 1; + string depositor = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string denom = 3; + string shares = 4 [ + (cosmos_proto.scalar) = "cosmos.Int", + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; +} diff --git a/proto/vaas/provider/v1/query.proto b/proto/vaas/provider/v1/query.proto index ab3c7be..c677213 100644 --- a/proto/vaas/provider/v1/query.proto +++ b/proto/vaas/provider/v1/query.proto @@ -13,6 +13,7 @@ import "tendermint/crypto/keys.proto"; import "cosmos_proto/cosmos.proto"; import "cosmos/staking/v1beta1/staking.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; +import "cosmos/base/v1beta1/coin.proto"; service Query { // ConsumerGenesis queries the genesis state needed to start a consumer chain @@ -106,6 +107,13 @@ service Query { option (google.api.http).get = "/vaas/provider/consumer_genesis_time/{consumer_id}"; } + + rpc ConsumerFeePoolClaim(QueryConsumerFeePoolClaimRequest) returns (QueryConsumerFeePoolClaimResponse) { + option (google.api.http).get = "/vaas/provider/v1/consumer_fee_pool_claim/{consumer_id}/{depositor}"; + } + rpc ConsumerFeePoolClaims(QueryConsumerFeePoolClaimsRequest) returns (QueryConsumerFeePoolClaimsResponse) { + option (google.api.http).get = "/vaas/provider/v1/consumer_fee_pool_claims/{consumer_id}"; + } } message QueryConsumerGenesisRequest { @@ -277,3 +285,36 @@ message QueryConsumerGenesisTimeResponse { google.protobuf.Timestamp genesis_time = 1 [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; } + +message QueryConsumerFeePoolClaimRequest { + uint64 consumer_id = 1; + // bech32 address; if equal to the gov module authority, aliases to the + // distribution module account address + string depositor = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +message QueryConsumerFeePoolClaimResponse { + // claimable balance across all denoms; excludes zero-claim denoms + repeated cosmos.base.v1beta1.Coin claim = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +message DepositorClaim { + string depositor = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + repeated cosmos.base.v1beta1.Coin claim = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +message QueryConsumerFeePoolClaimsRequest { + uint64 consumer_id = 1; + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +message QueryConsumerFeePoolClaimsResponse { + repeated DepositorClaim claims = 1 [(gogoproto.nullable) = false]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/proto/vaas/provider/v1/tx.proto b/proto/vaas/provider/v1/tx.proto index 3ac3677..17f7f93 100644 --- a/proto/vaas/provider/v1/tx.proto +++ b/proto/vaas/provider/v1/tx.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package vaas.provider.v1; import "amino/amino.proto"; +import "cosmos/base/v1beta1/coin.proto"; import "cosmos/msg/v1/msg.proto"; import "cosmos_proto/cosmos.proto"; import "gogoproto/gogo.proto"; @@ -27,6 +28,9 @@ service Msg { rpc UpdateConsumer(MsgUpdateConsumer) returns (MsgUpdateConsumerResponse); rpc RemoveConsumer(MsgRemoveConsumer) returns (MsgRemoveConsumerResponse); rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); + rpc FundConsumerFeePool(MsgFundConsumerFeePool) returns (MsgFundConsumerFeePoolResponse); + rpc WithdrawConsumerFeePool(MsgWithdrawConsumerFeePool) returns (MsgWithdrawConsumerFeePoolResponse); + rpc SweepConsumerFeePool(MsgSweepConsumerFeePool) returns (MsgSweepConsumerFeePoolResponse); } message MsgAssignConsumerKey { @@ -165,3 +169,72 @@ message MsgUpdateConsumer { // MsgUpdateConsumerResponse defines response type for MsgUpdateConsumer messages message MsgUpdateConsumerResponse {} + +// MsgFundConsumerFeePool deposits a single-denom amount into a consumer's +// fee pool and credits the signer with shares. If the signer is the gov +// module authority, funds are pulled from the community pool and the +// distribution module account is credited as the depositor. +message MsgFundConsumerFeePool { + option (cosmos.msg.v1.signer) = "signer"; + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + uint64 consumer_id = 2; + cosmos.base.v1beta1.Coin amount = 3 [ + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true + ]; +} + +message MsgFundConsumerFeePoolResponse {} + +// MsgWithdrawConsumerFeePool withdraws tokens from the signer's share in a +// consumer fee pool across one or more denoms. Each amount is interpreted as +// "up to this much": if the signer's claim for a denom is less than the +// requested amount the handler delivers the full claim and burns all of the +// signer's shares for that denom. The transaction is atomic: if the signer +// has no shares at all for any denom in `amount` (or the pool has zero +// balance for that denom), the whole tx aborts. +// If the signer is the gov module authority, the withdrawal targets the +// distribution module account's shares and tokens are routed back to the +// community pool. +message MsgWithdrawConsumerFeePool { + option (cosmos.msg.v1.signer) = "signer"; + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + uint64 consumer_id = 2; + repeated cosmos.base.v1beta1.Coin amount = 3 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (amino.dont_omitempty) = true + ]; +} + +message MsgWithdrawConsumerFeePoolResponse { + // total tokens actually delivered (may be less than requested due to + // truncation in the partial-withdraw branch) + repeated cosmos.base.v1beta1.Coin amount = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// MsgSweepConsumerFeePool distributes a consumer fee pool's balance pro-rata +// to all share-holders across the specified denoms (or all denoms if `denoms` +// is empty). Truncation residue per denom is forwarded to the community pool. +// Only the consumer owner may sign. +message MsgSweepConsumerFeePool { + option (cosmos.msg.v1.signer) = "signer"; + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + uint64 consumer_id = 2; + // empty = all denoms with shares or balance for this consumer + repeated string denoms = 3; +} + +message MsgSweepConsumerFeePoolResponse {} diff --git a/tests/e2e/e2e_debt_test.go b/tests/e2e/e2e_debt_test.go index fbace25..aa88eb7 100644 --- a/tests/e2e/e2e_debt_test.go +++ b/tests/e2e/e2e_debt_test.go @@ -4,6 +4,8 @@ import ( "encoding/json" "strings" "time" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // queryConsumerFeePoolAddress returns the provider-side fee pool account for a @@ -55,9 +57,15 @@ func (s *IntegrationTestSuite) consumerBankSendDryRun() (string, error) { return stderr.String(), err } -// providerFundAddress sends tokens from val to addr on the provider chain. +// providerFundAddress sends `amount` from val to `addr` on the provider chain +// and blocks until the recipient's balance for the funded denom has grown, +// so callers can immediately issue txs from `addr`. func (s *IntegrationTestSuite) providerFundAddress(addr, amount string) { - _, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + coin, err := sdk.ParseCoinNormalized(amount) + s.Require().NoError(err, "invalid amount %q", amount) + before := s.providerQueryBalance(addr, coin.Denom) + + _, _, err = s.dockerExec(s.providerValRes[0].Container.ID, []string{ providerBinary, "tx", "bank", "send", "val", addr, amount, "--from", "val", "--home", providerHomePath, @@ -67,6 +75,27 @@ func (s *IntegrationTestSuite) providerFundAddress(addr, amount string) { "-y", }) s.Require().NoError(err, "failed to fund provider address %s", addr) + + s.Require().Eventuallyf(func() bool { + return s.providerQueryBalance(addr, coin.Denom) > before + }, 30*time.Second, 2*time.Second, + "balance of %s in %s did not grow after fund (before=%d)", addr, coin.Denom, before) +} + +// providerFundConsumerFeePool deposits `amount` into the named consumer's +// fee pool via MsgFundConsumerFeePool, signed by val. +func (s *IntegrationTestSuite) providerFundConsumerFeePool(consumerID, amount string) { + _, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "tx", "provider", "fund-consumer-fee-pool", + consumerID, amount, + "--from", "val", + "--home", providerHomePath, + "--keyring-backend", "test", + "--chain-id", providerChainID, + "--fees", "10000" + bondDenom, + "-y", + }) + s.Require().NoError(err, "failed to fund consumer %s fee pool", consumerID) time.Sleep(3 * time.Second) } @@ -93,7 +122,7 @@ func (s *IntegrationTestSuite) testConsumerDebtFlow() { "consumer did not enter debt; last dry-run did not surface debt error") s.T().Log("funding consumer fee pool on provider...") - s.providerFundAddress(feePoolAddr, "10000000"+bondDenom) + s.providerFundConsumerFeePool("0", "10000000"+bondDenom) s.T().Log("waiting for consumer to exit debt (bank send should succeed)...") s.Require().Eventuallyf(func() bool { diff --git a/tests/e2e/e2e_fee_pool_test.go b/tests/e2e/e2e_fee_pool_test.go new file mode 100644 index 0000000..f611c00 --- /dev/null +++ b/tests/e2e/e2e_fee_pool_test.go @@ -0,0 +1,287 @@ +package e2e + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// providerQueryBalance returns the bech32-addressed account's balance for +// `denom` on the provider, as an int64. +func (s *IntegrationTestSuite) providerQueryBalance(addr, denom string) int64 { + stdout, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "query", "bank", "balances", addr, + "--home", providerHomePath, + "--output", "json", + }) + s.Require().NoError(err) + var res struct { + Balances []struct { + Denom string `json:"denom"` + Amount string `json:"amount"` + } `json:"balances"` + } + s.Require().NoError(json.Unmarshal(stdout.Bytes(), &res)) + for _, b := range res.Balances { + if b.Denom == denom { + var n int64 + fmt.Sscanf(b.Amount, "%d", &n) + return n + } + } + return 0 +} + +// providerQueryFeePoolClaim returns the depositor's claim on the consumer's +// fee pool in the given denom (returns 0 if no claim). +func (s *IntegrationTestSuite) providerQueryFeePoolClaim(consumerID, depositor, denom string) int64 { + stdout, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "query", "provider", "consumer-fee-pool-claim", + consumerID, depositor, + "--home", providerHomePath, + "--output", "json", + }) + s.Require().NoError(err) + var res struct { + Claim []struct { + Denom string `json:"denom"` + Amount string `json:"amount"` + } `json:"claim"` + } + s.Require().NoError(json.Unmarshal(stdout.Bytes(), &res)) + for _, c := range res.Claim { + if c.Denom == denom { + var n int64 + fmt.Sscanf(c.Amount, "%d", &n) + return n + } + } + return 0 +} + +// providerKeyAddress returns the bech32 address of the named key on the +// provider. +func (s *IntegrationTestSuite) providerKeyAddress(key string) string { + stdout, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "keys", "show", key, "-a", + "--home", providerHomePath, + "--keyring-backend", "test", + }) + s.Require().NoError(err) + return strings.TrimSpace(stdout.String()) +} + +// providerFundConsumerFeePoolFrom is the multi-signer variant of +// providerFundConsumerFeePool (which is hardcoded to --from val). +func (s *IntegrationTestSuite) providerFundConsumerFeePoolFrom(consumerID, from, amount string) { + _, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "tx", "provider", "fund-consumer-fee-pool", + consumerID, amount, + "--from", from, + "--home", providerHomePath, + "--keyring-backend", "test", + "--chain-id", providerChainID, + "--fees", "10000" + bondDenom, + "-y", + }) + s.Require().NoError(err) + time.Sleep(3 * time.Second) +} + +// providerWithdrawFeePool calls `tx provider withdraw-consumer-fee-pool`. +func (s *IntegrationTestSuite) providerWithdrawFeePool(consumerID, from, coins string) { + _, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "tx", "provider", "withdraw-consumer-fee-pool", + consumerID, coins, + "--from", from, + "--home", providerHomePath, + "--keyring-backend", "test", + "--chain-id", providerChainID, + "--fees", "10000" + bondDenom, + "-y", + }) + s.Require().NoError(err) + time.Sleep(3 * time.Second) +} + +// providerSweepFeePool calls `tx provider sweep-consumer-fee-pool`. +func (s *IntegrationTestSuite) providerSweepFeePool(consumerID, from string) { + _, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "tx", "provider", "sweep-consumer-fee-pool", + consumerID, + "--from", from, + "--home", providerHomePath, + "--keyring-backend", "test", + "--chain-id", providerChainID, + "--fees", "10000" + bondDenom, + "-y", + }) + s.Require().NoError(err) + time.Sleep(3 * time.Second) +} + +// providerFundCommunityPool funds the community pool from val on the provider +// and blocks until the community-pool balance for the funded denom has grown, +// so callers can immediately issue follow-up txs without racing the previous +// tx's account-sequence commit. +func (s *IntegrationTestSuite) providerFundCommunityPool(amount string) { + coin, err := sdk.ParseCoinNormalized(amount) + s.Require().NoError(err, "invalid amount %q", amount) + before := s.queryCommunityPoolBalance(coin.Denom) + + _, _, err = s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "tx", "distribution", "fund-community-pool", amount, + "--from", "val", + "--home", providerHomePath, + "--keyring-backend", "test", + "--chain-id", providerChainID, + "--fees", "10000" + bondDenom, + "-y", + }) + s.Require().NoError(err) + + s.Require().Eventuallyf(func() bool { + return s.queryCommunityPoolBalance(coin.Denom) > before + }, 30*time.Second, 2*time.Second, + "community pool %s did not grow after fund (before=%d)", coin.Denom, before) +} + +// testFeePoolFundWithdrawSweep exercises deposit + per-depositor withdraw + +// owner-triggered sweep with two distinct funders. val owns consumer 0 +// (registered during suite setup); user is a second funder. +// +// Assertions are deliberately tolerant: the consumer is in LAUNCHED phase, +// so CollectFeesFromConsumers is actively draining the pool every block. +// Exact equality would race the fee EndBlocker. The test verifies the +// wiring (CLI dispatches to handlers, share math runs, sweep distributes), +// not exact share-math results — those are covered by unit tests. +func (s *IntegrationTestSuite) testFeePoolFundWithdrawSweep() { + s.Run("fee pool fund/withdraw/sweep", func() { + const consumerID = "0" + denom := bondDenom + valAddr := s.providerKeyAddress("val") + userAddr := s.providerKeyAddress("user") + + // Make sure user has enough balance to fund + pay fees. + s.providerFundAddress(userAddr, "20000000"+denom) + + // Two funders. + s.providerFundConsumerFeePool(consumerID, "5000"+denom) // --from val + s.providerFundConsumerFeePoolFrom(consumerID, "user", "3000"+denom) // --from user + + valClaim := s.providerQueryFeePoolClaim(consumerID, valAddr, denom) + userClaim := s.providerQueryFeePoolClaim(consumerID, userAddr, denom) + s.Require().Greater(valClaim, int64(0), "val should have a non-zero claim") + s.Require().Greater(userClaim, int64(0), "user should have a non-zero claim") + + // Partial withdraw by val. Claim should shrink (or stay zero if fees + // consumed everything in the interim, which is unlikely but possible). + s.providerWithdrawFeePool(consumerID, "val", "1000"+denom) + valClaimAfter := s.providerQueryFeePoolClaim(consumerID, valAddr, denom) + s.Require().LessOrEqual(valClaimAfter, valClaim, "claim should not grow after a partial withdraw") + + // Owner sweep. Both should receive a share; their share records are + // gone afterward. + userBalBefore := s.providerQueryBalance(userAddr, denom) + s.providerSweepFeePool(consumerID, "val") + + s.Require().Eventuallyf(func() bool { + return s.providerQueryBalance(userAddr, denom) > userBalBefore + }, 30*time.Second, 2*time.Second, + "user should have received their proportional share from the sweep (before=%d)", userBalBefore) + + // Post-sweep, user's claim is zero (shares were burned). + s.Require().Eventuallyf(func() bool { + return s.providerQueryFeePoolClaim(consumerID, userAddr, denom) == 0 + }, 30*time.Second, 2*time.Second, + "user claim should be zero after sweep burned shares") + }) +} + +// testFeePoolSendRestriction verifies that direct bank sends to a consumer +// fee pool address are blocked by the SendRestriction. Uses --dry-run so +// the ante chain runs (which is where the restriction fires) without +// burning fees on a doomed transaction. +func (s *IntegrationTestSuite) testFeePoolSendRestriction() { + s.Run("fee pool send restriction", func() { + feePoolAddr := s.queryConsumerFeePoolAddress("0") + valAddr := s.providerKeyAddress("val") + + _, stderr, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "tx", "bank", "send", valAddr, feePoolAddr, "1" + bondDenom, + "--from", "val", + "--home", providerHomePath, + "--keyring-backend", "test", + "--chain-id", providerChainID, + "--fees", "10000" + bondDenom, + "--dry-run", + "-y", + }) + combined := stderr.String() + // The restriction's error message contains "consumer fee pool". + // Match on a distinctive substring rather than the exact error code + // so the test tolerates future error-message tweaks. + s.Require().True(err != nil || strings.Contains(combined, "consumer fee pool"), + "direct bank send to fee pool should be rejected: stderr=%q, err=%v", combined, err) + }) +} + +// testFeePoolGovSubsidyClawback verifies the gov-conditional path: +// gov can fund a consumer fee pool from the community pool (with the +// distribution module account credited as the depositor), and gov can +// claw back the unconsumed portion via a withdrawal proposal. +func (s *IntegrationTestSuite) testFeePoolGovSubsidyClawback() { + s.Run("fee pool gov subsidy + clawback", func() { + const consumerID = "0" + denom := bondDenom + govAddr := s.queryGovAuthority() + distrAddr := s.queryModuleAccountAddress("distribution") + + // Seed the community pool so gov has something to spend. + s.providerFundCommunityPool("10000000" + denom) + cpBefore := s.queryCommunityPoolBalance(denom) + s.Require().Greater(cpBefore, int64(0), "community pool seeded") + + fundJSON := fmt.Sprintf(`{ + "messages": [{ + "@type": "/vaas.provider.v1.MsgFundConsumerFeePool", + "signer": %q, + "consumer_id": %q, + "amount": {"denom": %q, "amount": "5000"} + }], + "metadata": "ipfs://test", + "deposit": "10000000%s", + "title": "Subsidize consumer %s", + "summary": "e2e gov subsidy test" +}`, govAddr, consumerID, denom, denom, consumerID) + + s.submitAndPassProposal(fundJSON) + + cpAfterFund := s.queryCommunityPoolBalance(denom) + s.Require().Less(cpAfterFund, cpBefore, "community pool debited by subsidy") + + distrClaim := s.providerQueryFeePoolClaim(consumerID, distrAddr, denom) + s.Require().Greater(distrClaim, int64(0), "distribution module account has a non-zero claim after gov fund") + + clawbackJSON := fmt.Sprintf(`{ + "messages": [{ + "@type": "/vaas.provider.v1.MsgWithdrawConsumerFeePool", + "signer": %q, + "consumer_id": %q, + "amount": [{"denom": %q, "amount": "5000"}] + }], + "metadata": "ipfs://test", + "deposit": "10000000%s", + "title": "Clawback consumer %s subsidy", + "summary": "e2e gov clawback test" +}`, govAddr, consumerID, denom, denom, consumerID) + + s.submitAndPassProposal(clawbackJSON) + + cpAfterClawback := s.queryCommunityPoolBalance(denom) + s.Require().Greater(cpAfterClawback, cpAfterFund, "community pool grew back after clawback") + }) +} diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index b176c90..7711d3a 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -8,6 +8,9 @@ func (s *IntegrationTestSuite) TestVAAS() { s.testProviderOnConsumer() s.testValidatorSetSync() s.testConsumerDebtFlow() + s.testFeePoolSendRestriction() + s.testFeePoolFundWithdrawSweep() + s.testFeePoolGovSubsidyClawback() // Run last: stops the provider container and replaces it with a fresh // one started from the exported genesis. s.testGenesisRoundTrip() diff --git a/tests/e2e/gov_proposal_helpers_test.go b/tests/e2e/gov_proposal_helpers_test.go new file mode 100644 index 0000000..15ed76d --- /dev/null +++ b/tests/e2e/gov_proposal_helpers_test.go @@ -0,0 +1,294 @@ +package e2e + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// queryModuleAccountAddress returns the bech32 address of the named module +// account on the provider chain. +func (s *IntegrationTestSuite) queryModuleAccountAddress(name string) string { + stdout, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "query", "auth", "module-account", name, + "--home", providerHomePath, + "--output", "json", + }) + s.Require().NoError(err, "failed to query module account %s", name) + + // JSON shape varies across SDK versions: try the common nests in order. + var res struct { + Account struct { + Address string `json:"address"` + BaseAccount struct { + Address string `json:"address"` + } `json:"base_account"` + Value struct { + Address string `json:"address"` + BaseAccount struct { + Address string `json:"address"` + } `json:"base_account"` + } `json:"value"` + } `json:"account"` + } + s.Require().NoError(json.Unmarshal(stdout.Bytes(), &res), + "failed to decode module-account response: %s", stdout.String()) + + candidates := []string{ + res.Account.BaseAccount.Address, + res.Account.Value.Address, + res.Account.Value.BaseAccount.Address, + res.Account.Address, + } + for _, addr := range candidates { + if addr != "" { + return addr + } + } + + s.T().Logf("module-account response for %s: %s", name, stdout.String()) + s.Require().Failf("module account address not found", "name=%s", name) + return "" +} + +// queryGovAuthority is a thin wrapper for queryModuleAccountAddress("gov"). +func (s *IntegrationTestSuite) queryGovAuthority() string { + return s.queryModuleAccountAddress("gov") +} + +// queryCommunityPoolBalance returns the current community-pool balance for the +// given denom, truncated to int64 (on-chain value is DecCoins). Returns 0 if +// denom not present. +func (s *IntegrationTestSuite) queryCommunityPoolBalance(denom string) int64 { + stdout, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "query", "distribution", "community-pool", + "--home", providerHomePath, + "--output", "json", + }) + s.Require().NoError(err, "failed to query community pool") + + var res struct { + Pool []string `json:"pool"` + } + s.Require().NoError(json.Unmarshal(stdout.Bytes(), &res), + "failed to decode community-pool response: %s", stdout.String()) + + for _, raw := range res.Pool { + dc, err := sdk.ParseDecCoin(raw) + if err != nil { + continue + } + if dc.Denom == denom { + return dc.Amount.TruncateInt64() + } + } + return 0 +} + +// submitAndPassProposal writes proposalJSON to /tmp/proposal.json in the +// provider container, submits the proposal from "val", votes YES from val, +// waits for tally to execute (15s voting period + a couple of blocks), and +// returns the proposal ID. Fails the test on submit/vote/tally errors. +// +// proposalJSON must be a valid gov v1 proposal body. The submitter and voting +// fees are paid in bondDenom by val. +func (s *IntegrationTestSuite) submitAndPassProposal(proposalJSON string) uint64 { + containerID := s.providerValRes[0].Container.ID + + // 1. Write the proposal body to /tmp/proposal.json via base64 to avoid + // shell-quoting headaches. + payload := base64.StdEncoding.EncodeToString([]byte(proposalJSON)) + _, _, err := s.dockerExec(containerID, []string{ + "sh", "-c", + fmt.Sprintf("echo %s | base64 -d > /tmp/proposal.json", payload), + }) + s.Require().NoError(err, "failed to write proposal.json") + + // 2. Submit the proposal. + stdout, stderr, err := s.dockerExec(containerID, []string{ + providerBinary, "tx", "gov", "submit-proposal", "/tmp/proposal.json", + "--from", "val", + "--home", providerHomePath, + "--keyring-backend", "test", + "--chain-id", providerChainID, + "--fees", "10000" + bondDenom, + "--yes", + "-o", "json", + }) + s.Require().NoErrorf(err, "failed to submit proposal: stdout=%s stderr=%s", + stdout.String(), stderr.String()) + + var submitRes struct { + TxHash string `json:"txhash"` + Code int `json:"code"` + RawLog string `json:"raw_log"` + } + s.Require().NoError(json.Unmarshal(stdout.Bytes(), &submitRes), + "failed to decode submit-proposal response: %s", stdout.String()) + s.Require().Equalf(0, submitRes.Code, "submit-proposal failed: %s", submitRes.RawLog) + s.Require().NotEmpty(submitRes.TxHash, "submit-proposal returned empty txhash: %s", stdout.String()) + + // Poll for the submit tx to land and yield a proposal_id. + var proposalID uint64 + var lastTxOut string + s.Require().Eventuallyf(func() bool { + txOut, _, qerr := s.dockerExec(containerID, []string{ + providerBinary, "query", "tx", submitRes.TxHash, + "--home", providerHomePath, + "--output", "json", + }) + if qerr != nil || txOut.Len() == 0 { + return false + } + lastTxOut = txOut.String() + proposalID = extractProposalID(txOut.Bytes()) + return proposalID != 0 + }, 30*time.Second, 2*time.Second, + "could not parse proposal_id from tx events for %s: last stdout=%s", + submitRes.TxHash, lastTxOut) + + // 3. Vote yes from val. + voteStdout, voteStderr, err := s.dockerExec(containerID, []string{ + providerBinary, "tx", "gov", "vote", fmt.Sprintf("%d", proposalID), "yes", + "--from", "val", + "--home", providerHomePath, + "--keyring-backend", "test", + "--chain-id", providerChainID, + "--fees", "10000" + bondDenom, + "--yes", + }) + s.Require().NoErrorf(err, "failed to vote on proposal %d: stdout=%s stderr=%s", + proposalID, voteStdout.String(), voteStderr.String()) + + // 4. Wait for tally. 30s timeout (15s voting + buffer), 2s tick. + s.Require().Eventuallyf(func() bool { + status := s.queryProposalStatus(proposalID) + switch status { + case "PROPOSAL_STATUS_PASSED": + return true + case "PROPOSAL_STATUS_REJECTED", "PROPOSAL_STATUS_FAILED": + body := s.dumpProposal(proposalID) + s.Require().Failf("proposal terminated unsuccessfully", + "proposal %d ended with status %s; full body: %s", + proposalID, status, body) + return true + default: + return false + } + }, 30*time.Second, 2*time.Second, + "proposal %d did not pass within timeout", proposalID) + + return proposalID +} + +// dumpProposal returns the raw JSON of a gov proposal query, used to surface +// the failed_reason / messages on terminal-bad status. +func (s *IntegrationTestSuite) dumpProposal(proposalID uint64) string { + stdout, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "query", "gov", "proposal", fmt.Sprintf("%d", proposalID), + "--home", providerHomePath, + "--output", "json", + }) + if err != nil { + return fmt.Sprintf("", err) + } + return stdout.String() +} + +// queryProposalStatus returns the textual status of a gov proposal (e.g. +// "PROPOSAL_STATUS_PASSED"). +func (s *IntegrationTestSuite) queryProposalStatus(proposalID uint64) string { + stdout, _, err := s.dockerExec(s.providerValRes[0].Container.ID, []string{ + providerBinary, "query", "gov", "proposal", fmt.Sprintf("%d", proposalID), + "--home", providerHomePath, + "--output", "json", + }) + if err != nil { + s.T().Logf("queryProposalStatus failed: %v", err) + return "" + } + var res struct { + Status string `json:"status"` + Proposal struct { + Status string `json:"status"` + } `json:"proposal"` + } + if err := json.Unmarshal(stdout.Bytes(), &res); err != nil { + s.T().Logf("queryProposalStatus failed: %v", err) + return "" + } + if res.Status != "" { + return res.Status + } + return res.Proposal.Status +} + +// extractProposalID walks a tx-query JSON response and returns the +// submit_proposal.proposal_id event attribute as uint64. Returns 0 if not +// found. +func extractProposalID(raw []byte) uint64 { + type event struct { + Type string `json:"type"` + Attributes []struct { + Key string `json:"key"` + Value string `json:"value"` + } `json:"attributes"` + } + var doc struct { + Events []event `json:"events"` + TxResponse struct { + Events []event `json:"events"` + Logs []struct { + Events []event `json:"events"` + } `json:"logs"` + } `json:"tx_response"` + Logs []struct { + Events []event `json:"events"` + } `json:"logs"` + } + if err := json.Unmarshal(raw, &doc); err != nil { + return 0 + } + + scan := func(events []event) uint64 { + for _, ev := range events { + if ev.Type != "submit_proposal" { + continue + } + for _, attr := range ev.Attributes { + if attr.Key == "proposal_id" { + v := strings.Trim(strings.TrimSpace(attr.Value), "\" ") + id, err := strconv.ParseUint(v, 10, 64) + if err != nil { + continue + } + return id + } + } + } + return 0 + } + + if id := scan(doc.Events); id != 0 { + return id + } + if id := scan(doc.TxResponse.Events); id != 0 { + return id + } + for _, lg := range doc.TxResponse.Logs { + if id := scan(lg.Events); id != 0 { + return id + } + } + for _, lg := range doc.Logs { + if id := scan(lg.Events); id != 0 { + return id + } + } + return 0 +} diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index 0f38214..57a20b9 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -692,6 +692,20 @@ func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { return m.recorder } +// GetAllBalances mocks base method. +func (m *MockBankKeeper) GetAllBalances(ctx context.Context, addr types.AccAddress) types.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr) + ret0, _ := ret[0].(types.Coins) + return ret0 +} + +// GetAllBalances indicates an expected call of GetAllBalances. +func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), ctx, addr) +} + // GetBalance mocks base method. func (m *MockBankKeeper) GetBalance(ctx context.Context, addr types.AccAddress, denom string) types.Coin { m.ctrl.T.Helper() @@ -877,3 +891,55 @@ func (mr *MockIBCTransferKeeperMockRecorder) Transfer(arg0, arg1 any) *gomock.Ca mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transfer", reflect.TypeOf((*MockIBCTransferKeeper)(nil).Transfer), arg0, arg1) } + +// MockDistributionKeeper is a mock of DistributionKeeper interface. +type MockDistributionKeeper struct { + ctrl *gomock.Controller + recorder *MockDistributionKeeperMockRecorder + isgomock struct{} +} + +// MockDistributionKeeperMockRecorder is the mock recorder for MockDistributionKeeper. +type MockDistributionKeeperMockRecorder struct { + mock *MockDistributionKeeper +} + +// NewMockDistributionKeeper creates a new mock instance. +func NewMockDistributionKeeper(ctrl *gomock.Controller) *MockDistributionKeeper { + mock := &MockDistributionKeeper{ctrl: ctrl} + mock.recorder = &MockDistributionKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDistributionKeeper) EXPECT() *MockDistributionKeeperMockRecorder { + return m.recorder +} + +// DistributeFromFeePool mocks base method. +func (m *MockDistributionKeeper) DistributeFromFeePool(ctx context.Context, amount types.Coins, receiveAddr types.AccAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DistributeFromFeePool", ctx, amount, receiveAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// DistributeFromFeePool indicates an expected call of DistributeFromFeePool. +func (mr *MockDistributionKeeperMockRecorder) DistributeFromFeePool(ctx, amount, receiveAddr any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DistributeFromFeePool", reflect.TypeOf((*MockDistributionKeeper)(nil).DistributeFromFeePool), ctx, amount, receiveAddr) +} + +// FundCommunityPool mocks base method. +func (m *MockDistributionKeeper) FundCommunityPool(ctx context.Context, amount types.Coins, sender types.AccAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FundCommunityPool", ctx, amount, sender) + ret0, _ := ret[0].(error) + return ret0 +} + +// FundCommunityPool indicates an expected call of FundCommunityPool. +func (mr *MockDistributionKeeperMockRecorder) FundCommunityPool(ctx, amount, sender any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FundCommunityPool", reflect.TypeOf((*MockDistributionKeeper)(nil).FundCommunityPool), ctx, amount, sender) +} diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index 4c00289..be2d657 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -75,17 +75,19 @@ type MockedKeepers struct { *MockSlashingKeeper *MockAccountKeeper *MockBankKeeper + *MockDistributionKeeper } // NewMockedKeepers instantiates a struct with pointers to properly instantiated mocked keepers. func NewMockedKeepers(ctrl *gomock.Controller) MockedKeepers { mocks := MockedKeepers{ - MockClientKeeper: NewMockClientKeeper(ctrl), - MockClientV2Keeper: NewMockClientV2Keeper(ctrl), - MockStakingKeeper: NewMockStakingKeeper(ctrl), - MockSlashingKeeper: NewMockSlashingKeeper(ctrl), - MockAccountKeeper: NewMockAccountKeeper(ctrl), - MockBankKeeper: NewMockBankKeeper(ctrl), + MockClientKeeper: NewMockClientKeeper(ctrl), + MockClientV2Keeper: NewMockClientV2Keeper(ctrl), + MockStakingKeeper: NewMockStakingKeeper(ctrl), + MockSlashingKeeper: NewMockSlashingKeeper(ctrl), + MockAccountKeeper: NewMockAccountKeeper(ctrl), + MockBankKeeper: NewMockBankKeeper(ctrl), + MockDistributionKeeper: NewMockDistributionKeeper(ctrl), } mocks.MockClientV2Keeper.EXPECT().GetClientCounterparty(gomock.Any(), gomock.Any()).Return(clientv2types.CounterpartyInfo{}, false).AnyTimes() mocks.MockClientV2Keeper.EXPECT().SetClientCounterparty(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() @@ -104,6 +106,7 @@ func NewInMemProviderKeeper(params InMemKeeperParams, mocks MockedKeepers) provi mocks.MockSlashingKeeper, mocks.MockAccountKeeper, mocks.MockBankKeeper, + mocks.MockDistributionKeeper, govkeeper.Keeper{}, // HACK: to make parts of the test work authtypes.NewModuleAddress(govtypes.ModuleName).String(), address.NewBech32Codec("cosmosvaloper"), diff --git a/x/vaas/provider/client/cli/query.go b/x/vaas/provider/client/cli/query.go index 5a6ee8d..7c5e6d5 100644 --- a/x/vaas/provider/client/cli/query.go +++ b/x/vaas/provider/client/cli/query.go @@ -44,6 +44,8 @@ func NewQueryCmd() *cobra.Command { cmd.AddCommand(CmdConsumerIdFromClientId()) cmd.AddCommand(CmdConsumerChain()) cmd.AddCommand(CmdConsumerGenesisTime()) + cmd.AddCommand(CmdConsumerFeePoolClaim()) + cmd.AddCommand(CmdConsumerFeePoolClaims()) return cmd } @@ -449,3 +451,66 @@ func CmdConsumerGenesisTime() *cobra.Command { return cmd } + +func CmdConsumerFeePoolClaim() *cobra.Command { + cmd := &cobra.Command{ + Use: "consumer-fee-pool-claim [consumer-id] [depositor]", + Short: "Query a depositor's claimable balance on a consumer fee pool", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + consumerId, err := parseConsumerIdArg(args[0]) + if err != nil { + return err + } + qc := types.NewQueryClient(clientCtx) + res, err := qc.ConsumerFeePoolClaim(cmd.Context(), &types.QueryConsumerFeePoolClaimRequest{ + ConsumerId: consumerId, + Depositor: args[1], + }) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +func CmdConsumerFeePoolClaims() *cobra.Command { + cmd := &cobra.Command{ + Use: "consumer-fee-pool-claims [consumer-id]", + Short: "Query all depositor claims on a consumer fee pool (paginated)", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + consumerId, err := parseConsumerIdArg(args[0]) + if err != nil { + return err + } + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + qc := types.NewQueryClient(clientCtx) + res, err := qc.ConsumerFeePoolClaims(cmd.Context(), &types.QueryConsumerFeePoolClaimsRequest{ + ConsumerId: consumerId, + Pagination: pageReq, + }) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + flags.AddPaginationFlagsToCmd(cmd, "consumer-fee-pool-claims") + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/vaas/provider/client/cli/tx.go b/x/vaas/provider/client/cli/tx.go index 865b5d9..88e23e9 100644 --- a/x/vaas/provider/client/cli/tx.go +++ b/x/vaas/provider/client/cli/tx.go @@ -37,6 +37,9 @@ func GetTxCmd() *cobra.Command { cmd.AddCommand(NewCreateConsumerCmd()) cmd.AddCommand(NewUpdateConsumerCmd()) cmd.AddCommand(NewRemoveConsumerCmd()) + cmd.AddCommand(NewFundConsumerFeePoolCmd()) + cmd.AddCommand(NewWithdrawConsumerFeePoolCmd()) + cmd.AddCommand(NewSweepConsumerFeePoolCmd()) return cmd } @@ -456,3 +459,104 @@ Example: return cmd } + +func NewFundConsumerFeePoolCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "fund-consumer-fee-pool [consumer-id] [amount]", + Short: "Deposit funds into a consumer's fee pool, crediting the signer with shares", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + consumerId, err := parseConsumerIdArg(args[0]) + if err != nil { + return err + } + amount, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return err + } + msg := &types.MsgFundConsumerFeePool{ + Signer: clientCtx.GetFromAddress().String(), + ConsumerId: consumerId, + Amount: amount, + } + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + _ = cmd.MarkFlagRequired(flags.FlagFrom) + return cmd +} + +func NewWithdrawConsumerFeePoolCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-consumer-fee-pool [consumer-id] [coins]", + Short: "Withdraw tokens from your share in a consumer's fee pool (multi-denom)", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + consumerId, err := parseConsumerIdArg(args[0]) + if err != nil { + return err + } + coins, err := sdk.ParseCoinsNormalized(args[1]) + if err != nil { + return err + } + msg := &types.MsgWithdrawConsumerFeePool{ + Signer: clientCtx.GetFromAddress().String(), + ConsumerId: consumerId, + Amount: coins, + } + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + _ = cmd.MarkFlagRequired(flags.FlagFrom) + return cmd +} + +func NewSweepConsumerFeePoolCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "sweep-consumer-fee-pool [consumer-id]", + Short: "Owner-triggered pro-rata distribution of a consumer's fee pool", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + consumerId, err := parseConsumerIdArg(args[0]) + if err != nil { + return err + } + denoms, _ := cmd.Flags().GetStringSlice("denoms") + msg := &types.MsgSweepConsumerFeePool{ + Signer: clientCtx.GetFromAddress().String(), + ConsumerId: consumerId, + Denoms: denoms, + } + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + cmd.Flags().StringSlice("denoms", nil, + "denoms to sweep; repeat flag or pass comma-separated (default: all denoms with shares or balance)") + flags.AddTxFlagsToCmd(cmd) + _ = cmd.MarkFlagRequired(flags.FlagFrom) + return cmd +} diff --git a/x/vaas/provider/keeper/consumer_lifecycle.go b/x/vaas/provider/keeper/consumer_lifecycle.go index d4d9360..cbd5877 100644 --- a/x/vaas/provider/keeper/consumer_lifecycle.go +++ b/x/vaas/provider/keeper/consumer_lifecycle.go @@ -394,6 +394,10 @@ func (k Keeper) DeleteConsumerChain(ctx sdk.Context, consumerId uint64) (err err return fmt.Errorf("cannot delete non-stopped chain: %d", consumerId) } + if err := k.SweepConsumerFeePool(ctx, consumerId, nil); err != nil { + return err + } + // clean up states k.DeleteConsumerClientId(ctx, consumerId) k.DeleteConsumerGenesis(ctx, consumerId) @@ -409,6 +413,10 @@ func (k Keeper) DeleteConsumerChain(ctx sdk.Context, consumerId uint64) (err err k.DeleteConsumerRemovalTime(ctx, consumerId) k.DeleteConsumerDebt(ctx, consumerId) + if err := k.FeePoolAddressToConsumerId.Remove(ctx, k.GetConsumerFeePoolAddress(consumerId)); err != nil { + return err + } + // TODO (PERMISSIONLESS) add newly-added state to be deleted // Note that we do not delete ConsumerIdToChainIdKey and ConsumerIdToPhase, as well diff --git a/x/vaas/provider/keeper/consumer_lifecycle_test.go b/x/vaas/provider/keeper/consumer_lifecycle_test.go new file mode 100644 index 0000000..6cdaba4 --- /dev/null +++ b/x/vaas/provider/keeper/consumer_lifecycle_test.go @@ -0,0 +1,239 @@ +package keeper_test + +import ( + "fmt" + "testing" + "time" + + "cosmossdk.io/collections" + "cosmossdk.io/math" + + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + testkeeper "github.com/allinbits/vaas/testutil/keeper" + providertypes "github.com/allinbits/vaas/x/vaas/provider/types" +) + +func TestDeleteConsumerChain_RemovesReverseLookup(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := k.FetchAndIncrementConsumerId(ctx) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolAddr, consumerId)) + k.SetConsumerClientId(ctx, consumerId, "07-tendermint-0") + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_STOPPED) + + mocks.MockBankKeeper.EXPECT().GetAllBalances(ctx, poolAddr).Return(sdk.NewCoins()) + + require.NoError(t, k.DeleteConsumerChain(ctx, consumerId)) + + _, err := k.FeePoolAddressToConsumerId.Get(ctx, poolAddr) + require.ErrorIs(t, err, collections.ErrNotFound) +} + +func TestDeleteConsumerChain_AutoSweep(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerClientId(ctx, consumerId, "07-tendermint-0") // required for cleanup block + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_STOPPED) + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolAddr, consumerId)) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(100))) + + mocks.MockBankKeeper.EXPECT().GetAllBalances(ctx, poolAddr). + Return(sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 100)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providertypes.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, alice, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100))).Return(nil) + + require.NoError(t, k.DeleteConsumerChain(ctx, consumerId)) + + require.Equal(t, providertypes.CONSUMER_PHASE_DELETED, k.GetConsumerPhase(ctx, consumerId)) +} + +// TestDeleteConsumerChain_AutoSweepMultiDenomDust verifies that auto-sweep +// during consumer delete handles multiple denoms with truncation dust +// correctly: each share-holder gets their floor-rounded slice, the residue +// goes to the community pool, and the reverse-lookup is removed. +func TestDeleteConsumerChain_AutoSweepMultiDenomDust(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerClientId(ctx, consumerId, "07-tendermint-0") + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_STOPPED) + + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + providerAddr := authtypes.NewModuleAddress(providertypes.ModuleName) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolAddr, consumerId)) + + // uphoton: 3 shares total (alice=1, bob=2) against balance 10 -> alice + // gets floor(1*10/3)=3, bob gets floor(2*10/3)=6, dust 1. + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", alice), math.NewInt(1))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", bob), math.NewInt(2))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(3))) + + // uatone: 7 shares total (alice=4, bob=3) against balance 20 -> alice + // gets floor(4*20/7)=11, bob gets floor(3*20/7)=8, dust 1. + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uatone", alice), math.NewInt(4))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uatone", bob), math.NewInt(3))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uatone"), math.NewInt(7))) + + mocks.MockBankKeeper.EXPECT().GetAllBalances(ctx, poolAddr).Return(sdk.NewCoins( + sdk.NewInt64Coin("uphoton", 10), + sdk.NewInt64Coin("uatone", 20), + )) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 10)) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uatone"). + Return(sdk.NewInt64Coin("uatone", 20)) + + // Per-denom pool drain + per-holder send + dust to community pool. + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providertypes.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin("uatone", 20))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, alice, + sdk.NewCoins(sdk.NewInt64Coin("uatone", 11))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, bob, + sdk.NewCoins(sdk.NewInt64Coin("uatone", 8))).Return(nil) + mocks.MockDistributionKeeper.EXPECT().FundCommunityPool( + ctx, sdk.NewCoins(sdk.NewInt64Coin("uatone", 1)), providerAddr).Return(nil) + + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providertypes.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin("uphoton", 10))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, alice, + sdk.NewCoins(sdk.NewInt64Coin("uphoton", 3))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, bob, + sdk.NewCoins(sdk.NewInt64Coin("uphoton", 6))).Return(nil) + mocks.MockDistributionKeeper.EXPECT().FundCommunityPool( + ctx, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 1)), providerAddr).Return(nil) + + require.NoError(t, k.DeleteConsumerChain(ctx, consumerId)) + + require.Equal(t, providertypes.CONSUMER_PHASE_DELETED, k.GetConsumerPhase(ctx, consumerId)) + + // Reverse-lookup entry removed. + _, err := k.FeePoolAddressToConsumerId.Get(ctx, poolAddr) + require.ErrorIs(t, err, collections.ErrNotFound) + + // All share records cleared. + _, err = k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, "uphoton", alice)) + require.ErrorIs(t, err, collections.ErrNotFound) + _, err = k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, "uatone", bob)) + require.ErrorIs(t, err, collections.ErrNotFound) +} + +// TestBeginBlockRemoveConsumers_PerConsumerRollback verifies that a +// failing delete on one consumer (sweep returns an error) does not bleed +// state mutations into siblings whose deletes succeed in the same block. +// The first consumer's sweep fails; the second's succeeds. After the call, +// the first stays in STOPPED with its state intact and the second is gone. +func TestBeginBlockRemoveConsumers_PerConsumerRollback(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + removalTime := time.Unix(1000, 0) + ctx = ctx.WithBlockTime(removalTime.Add(time.Hour)) + + // failing consumer: balance + shares present, bank-pull errors out. + failId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, failId, providertypes.CONSUMER_PHASE_STOPPED) + require.NoError(t, k.SetConsumerRemovalTime(ctx, failId, removalTime)) + require.NoError(t, k.AppendConsumerToBeRemoved(ctx, failId, removalTime)) + failPoolAddr := k.GetConsumerFeePoolAddress(failId) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, failPoolAddr, failId)) + failDepositor := sdk.AccAddress([]byte("fail-depositor__")) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(failId, "uphoton", failDepositor), math.NewInt(50))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(failId, "uphoton"), math.NewInt(50))) + + // succeeding consumer: pool empty, cleanup succeeds. + okId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerClientId(ctx, okId, "07-tendermint-0") + k.SetConsumerPhase(ctx, okId, providertypes.CONSUMER_PHASE_STOPPED) + require.NoError(t, k.SetConsumerRemovalTime(ctx, okId, removalTime)) + require.NoError(t, k.AppendConsumerToBeRemoved(ctx, okId, removalTime)) + okPoolAddr := k.GetConsumerFeePoolAddress(okId) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, okPoolAddr, okId)) + + // BeginBlockRemoveConsumers wraps each per-consumer delete in a + // cache-context, so match the ctx argument with gomock.Any(). + mocks.MockBankKeeper.EXPECT().GetAllBalances(gomock.Any(), failPoolAddr). + Return(sdk.NewCoins(sdk.NewInt64Coin("uphoton", 50))) + mocks.MockBankKeeper.EXPECT().GetBalance(gomock.Any(), failPoolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 50)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + gomock.Any(), failPoolAddr, providertypes.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin("uphoton", 50))). + Return(fmt.Errorf("forced bank error")) + + mocks.MockBankKeeper.EXPECT().GetAllBalances(gomock.Any(), okPoolAddr).Return(sdk.NewCoins()) + + require.NoError(t, k.BeginBlockRemoveConsumers(ctx)) + + // Failing consumer: state preserved (cache-context rolled back). + require.Equal(t, providertypes.CONSUMER_PHASE_STOPPED, k.GetConsumerPhase(ctx, failId)) + _, err := k.FeePoolAddressToConsumerId.Get(ctx, failPoolAddr) + require.NoError(t, err, "failing consumer's reverse-lookup entry should remain") + + // Succeeding consumer: deleted. + require.Equal(t, providertypes.CONSUMER_PHASE_DELETED, k.GetConsumerPhase(ctx, okId)) + _, err = k.FeePoolAddressToConsumerId.Get(ctx, okPoolAddr) + require.ErrorIs(t, err, collections.ErrNotFound) +} + +func TestDeleteConsumerChain_AutoSweepFailureAborts(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := k.FetchAndIncrementConsumerId(ctx) + // No SetConsumerClientId — sweep fails before the cleanup block runs + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_STOPPED) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + depositor := sdk.AccAddress([]byte("alice___________")) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", depositor), math.NewInt(50))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(50))) + + mocks.MockBankKeeper.EXPECT().GetAllBalances(ctx, poolAddr). + Return(sdk.NewCoins(sdk.NewInt64Coin("uphoton", 50))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 50)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providertypes.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 50))). + Return(fmt.Errorf("forced bank error")) + + err := k.DeleteConsumerChain(ctx, consumerId) + require.Error(t, err) + // Phase remains STOPPED, not DELETED + require.Equal(t, providertypes.CONSUMER_PHASE_STOPPED, k.GetConsumerPhase(ctx, consumerId)) +} diff --git a/x/vaas/provider/keeper/fee_pool_shares.go b/x/vaas/provider/keeper/fee_pool_shares.go new file mode 100644 index 0000000..601fbf2 --- /dev/null +++ b/x/vaas/provider/keeper/fee_pool_shares.go @@ -0,0 +1,369 @@ +package keeper + +import ( + "fmt" + "sort" + "strconv" + + "github.com/allinbits/vaas/x/vaas/provider/types" + + "cosmossdk.io/collections" + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// emitSweepEvent emits one ConsumerFeePoolSweep event per swept denom. +func (k Keeper) emitSweepEvent( + ctx sdk.Context, consumerId uint64, denom string, distributed, dust math.Int, +) { + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeConsumerFeePoolSweep, + sdk.NewAttribute(types.AttributeConsumerId, strconv.FormatUint(consumerId, 10)), + sdk.NewAttribute(types.AttributeDenom, denom), + sdk.NewAttribute(types.AttributeTotalDistributed, distributed.String()), + sdk.NewAttribute(types.AttributeDust, dust.String()), + )) +} + +// ComputeClaim returns the depositor's claimable tokens for one denom on the +// given consumer's fee pool. Returns math.ZeroInt() if the depositor has no +// shares or total_shares is zero. +func (k Keeper) ComputeClaim( + ctx sdk.Context, consumerId uint64, depositor sdk.AccAddress, denom string, +) math.Int { + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + balance := k.bankKeeper.GetBalance(ctx, poolAddr, denom) + if balance.Amount.IsZero() { + return math.ZeroInt() + } + shares, err := k.ConsumerFeePoolShares.Get(ctx, + collections.Join3(consumerId, denom, depositor)) + if err != nil { + return math.ZeroInt() + } + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + if err != nil || total.IsZero() { + return math.ZeroInt() + } + // claim = floor(shares * balance / total) + return shares.Mul(balance.Amount).Quo(total) +} + +// MintShares credits the depositor with shares for the given amount in the +// specified consumer's fee pool. Handles the lazy-invalidation case: +// if balance == 0 but total_shares > 0, all existing shares for this +// (consumer, denom) are deleted first (they represent worthless claims). +// +// Caller is responsible for the bank-side movement of funds into the pool. +func (k Keeper) MintShares( + ctx sdk.Context, consumerId uint64, depositor sdk.AccAddress, amount sdk.Coin, +) error { + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + balance := k.bankKeeper.GetBalance(ctx, poolAddr, amount.Denom) + + totalKey := collections.Join(consumerId, amount.Denom) + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, totalKey) + if err != nil { + total = math.ZeroInt() + } + + // Lazy invalidation: balance == 0 with leftover shares means everyone's + // claim is zero. Clear stale shares before treating as initial deposit. + if balance.Amount.IsZero() && total.IsPositive() { + if err := k.clearAllShares(ctx, consumerId, amount.Denom); err != nil { + return err + } + total = math.ZeroInt() + } + + // balance > 0 with no shares should be unreachable: every code path that + // adds balance also mints shares, and the send-restriction blocks any + // other deposit. InitGenesis catches the same condition on import. If we + // see it here mid-life, the state machine is corrupted. + if total.IsZero() && balance.Amount.IsPositive() { + panic(fmt.Sprintf( + "fee-pool invariant violated: consumer %d denom %s has balance %s but no shares", + consumerId, amount.Denom, balance.Amount.String(), + )) + } + + var shares math.Int + if total.IsZero() { + shares = amount.Amount + } else { + // shares_to_mint = amount * total / balance + // (balance is balance BEFORE this deposit lands) + shares = amount.Amount.Mul(total).Quo(balance.Amount) + } + if !shares.IsPositive() { + // sub-share deposit (extreme dilution) — should be very rare but + // refuse rather than silently dropping + return errorsmod.Wrap(types.ErrDepositTooSmall, + "deposit too small to mint any shares") + } + + depKey := collections.Join3(consumerId, amount.Denom, depositor) + existing, err := k.ConsumerFeePoolShares.Get(ctx, depKey) + if err != nil { + existing = math.ZeroInt() + } + if err := k.ConsumerFeePoolShares.Set(ctx, depKey, existing.Add(shares)); err != nil { + return err + } + return k.ConsumerFeePoolTotalShares.Set(ctx, totalKey, total.Add(shares)) +} + +// WithdrawShares burns shares for the given depositor and returns the tokens +// to send. If amount >= claim, burns all of the depositor's shares (full +// branch). Otherwise burns a proportional amount (partial branch), truncated +// downward so the depositor never over-withdraws. Caller is responsible for +// dispatching the bank send. +func (k Keeper) WithdrawShares( + ctx sdk.Context, consumerId uint64, depositor sdk.AccAddress, amount sdk.Coin, +) (sdk.Coin, error) { + depKey := collections.Join3(consumerId, amount.Denom, depositor) + shares, err := k.ConsumerFeePoolShares.Get(ctx, depKey) + if err != nil { + return sdk.Coin{}, errorsmod.Wrapf(types.ErrNoSharesForDepositor, + "depositor %s has no shares in (%d, %s)", depositor, consumerId, amount.Denom) + } + totalKey := collections.Join(consumerId, amount.Denom) + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, totalKey) + if err != nil || total.IsZero() { + return sdk.Coin{}, errorsmod.Wrap(types.ErrPoolEmpty, + "no shares accounted for this denom") + } + + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + balance := k.bankKeeper.GetBalance(ctx, poolAddr, amount.Denom) + if balance.Amount.IsZero() { + return sdk.Coin{}, errorsmod.Wrapf(types.ErrPoolEmpty, + "pool has zero balance for denom %s", amount.Denom) + } + + claim := shares.Mul(balance.Amount).Quo(total) + + var sharesToBurn, tokensToSend math.Int + if amount.Amount.GTE(claim) { + // Full branch: burn all shares, deliver exact claim + sharesToBurn = shares + tokensToSend = claim + } else { + // Partial branch: shares_to_burn = floor(amount * total / balance) + sharesToBurn = amount.Amount.Mul(total).Quo(balance.Amount) + if sharesToBurn.IsZero() { + return sdk.Coin{}, errorsmod.Wrapf(types.ErrSubShareWithdraw, + "requested %s but pool is too diluted to burn any shares", + amount.String()) + } + tokensToSend = sharesToBurn.Mul(balance.Amount).Quo(total) + } + + remainingShares := shares.Sub(sharesToBurn) + if remainingShares.IsZero() { + if err := k.ConsumerFeePoolShares.Remove(ctx, depKey); err != nil { + return sdk.Coin{}, err + } + } else { + if err := k.ConsumerFeePoolShares.Set(ctx, depKey, remainingShares); err != nil { + return sdk.Coin{}, err + } + } + + newTotal := total.Sub(sharesToBurn) + if newTotal.IsZero() { + if err := k.ConsumerFeePoolTotalShares.Remove(ctx, totalKey); err != nil { + return sdk.Coin{}, err + } + } else { + if err := k.ConsumerFeePoolTotalShares.Set(ctx, totalKey, newTotal); err != nil { + return sdk.Coin{}, err + } + } + + return sdk.NewCoin(amount.Denom, tokensToSend), nil +} + +// SweepConsumerFeePoolDenom drains the consumer's pool for the given denom, +// distributing pro-rata to all share-holders and routing the truncation +// residue to the community pool. Share records and total for the +// (consumer, denom) pair are deleted. +// +// Distribution to the distribution module account uses FundCommunityPool +// rather than a raw bank send, so the community pool's FeePool DecCoins are +// credited correctly. +func (k Keeper) SweepConsumerFeePoolDenom( + ctx sdk.Context, consumerId uint64, denom string, +) error { + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + balance := k.bankKeeper.GetBalance(ctx, poolAddr, denom) + totalKey := collections.Join(consumerId, denom) + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, totalKey) + if err != nil { + total = math.ZeroInt() + } + + if balance.Amount.IsZero() && total.IsZero() { + return nil + } + + // Orphan balance: should be unreachable (see MintShares; InitGenesis + // catches the import case). Panic in case it happens. + if total.IsZero() { + panic(fmt.Sprintf( + "fee-pool invariant violated: consumer %d denom %s has balance %s but no shares", + consumerId, denom, balance.Amount.String(), + )) + } + + // Orphan shares: shares > 0 but balance == 0. Burn all shares, no transfer. + if balance.Amount.IsZero() { + if err := k.clearAllShares(ctx, consumerId, denom); err != nil { + return err + } + k.emitSweepEvent(ctx, consumerId, denom, math.ZeroInt(), math.ZeroInt()) + return nil + } + + providerModule := types.ModuleName + providerAddr := authtypes.NewModuleAddress(providerModule) + distrAddr := authtypes.NewModuleAddress(disttypes.ModuleName) + + // Move full balance into provider module account in one hop. + if err := k.bankKeeper.SendCoinsFromAccountToModule( + ctx, poolAddr, providerModule, sdk.NewCoins(balance), + ); err != nil { + return err + } + + // One pass: iterate share records, distribute each slice, then clear + // records in a final clearAllShares (which buffers keys before deleting + // so the in-flight iterator is not invalidated). + distributed := math.ZeroInt() + prefix := collections.NewSuperPrefixedTripleRange[uint64, string, sdk.AccAddress](consumerId, denom) + iter, err := k.ConsumerFeePoolShares.Iterate(ctx, prefix) + if err != nil { + return err + } + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + iter.Close() + return err + } + shares, err := iter.Value() + if err != nil { + iter.Close() + return err + } + slice := shares.Mul(balance.Amount).Quo(total) + if slice.IsZero() { + continue + } + coins := sdk.NewCoins(sdk.NewCoin(denom, slice)) + addr := key.K3() + if addr.Equals(distrAddr) { + if err := k.distributionKeeper.FundCommunityPool(ctx, coins, providerAddr); err != nil { + iter.Close() + return err + } + } else { + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, providerModule, addr, coins, + ); err != nil { + iter.Close() + return err + } + } + distributed = distributed.Add(slice) + } + iter.Close() + + // Truncation residue -> community pool. + dust := balance.Amount.Sub(distributed) + if dust.IsPositive() { + if err := k.distributionKeeper.FundCommunityPool( + ctx, sdk.NewCoins(sdk.NewCoin(denom, dust)), providerAddr, + ); err != nil { + return err + } + } + + if err := k.clearAllShares(ctx, consumerId, denom); err != nil { + return err + } + k.emitSweepEvent(ctx, consumerId, denom, distributed, dust) + return nil +} + +// SweepConsumerFeePool sweeps each denom in `denoms`, or every denom that +// has either non-zero shares or non-zero pool balance if `denoms` is nil/empty. +func (k Keeper) SweepConsumerFeePool( + ctx sdk.Context, consumerId uint64, denoms []string, +) error { + if len(denoms) > 0 { + for _, d := range denoms { + if err := k.SweepConsumerFeePoolDenom(ctx, consumerId, d); err != nil { + return err + } + } + return nil + } + + // Union of denoms-with-shares and denoms-with-balance. + set := map[string]struct{}{} + prefix := collections.NewPrefixedPairRange[uint64, string](consumerId) + iter, err := k.ConsumerFeePoolTotalShares.Iterate(ctx, prefix) + if err != nil { + return err + } + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + iter.Close() + return err + } + set[key.K2()] = struct{}{} + } + iter.Close() + + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + for _, c := range k.bankKeeper.GetAllBalances(ctx, poolAddr) { + set[c.Denom] = struct{}{} + } + + // Deterministic iteration. + keys := make([]string, 0, len(set)) + for d := range set { + keys = append(keys, d) + } + sort.Strings(keys) + for _, d := range keys { + if err := k.SweepConsumerFeePoolDenom(ctx, consumerId, d); err != nil { + return err + } + } + return nil +} + +// clearAllShares deletes every share record for the given (consumer, denom) +// and the matching total_shares entry. Used by lazy invalidation and by +// sweep finalization. +func (k Keeper) clearAllShares(ctx sdk.Context, consumerId uint64, denom string) error { + if err := k.ConsumerFeePoolShares.Clear(ctx, + collections.NewSuperPrefixedTripleRange[uint64, string, sdk.AccAddress](consumerId, denom), + ); err != nil { + return err + } + totalKey := collections.Join(consumerId, denom) + if has, err := k.ConsumerFeePoolTotalShares.Has(ctx, totalKey); err != nil { + return err + } else if !has { + return nil + } + return k.ConsumerFeePoolTotalShares.Remove(ctx, totalKey) +} diff --git a/x/vaas/provider/keeper/fee_pool_shares_test.go b/x/vaas/provider/keeper/fee_pool_shares_test.go new file mode 100644 index 0000000..03614b8 --- /dev/null +++ b/x/vaas/provider/keeper/fee_pool_shares_test.go @@ -0,0 +1,445 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/collections" + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + + "github.com/stretchr/testify/require" + + testkeeper "github.com/allinbits/vaas/testutil/keeper" + providertypes "github.com/allinbits/vaas/x/vaas/provider/types" +) + +func TestComputeClaim(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + + // No shares yet: claim is zero + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 0)) + require.True(t, k.ComputeClaim(ctx, consumerId, alice, denom).IsZero()) + + // Seed: alice has 100 shares of 100 total, balance 50 + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 50)) + require.Equal(t, math.NewInt(50), k.ComputeClaim(ctx, consumerId, alice, denom)) +} + +func TestMintShares_Initial(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // Initial deposit: total_shares == 0; mint = amount + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 0)) + require.NoError(t, k.MintShares(ctx, consumerId, alice, sdk.NewInt64Coin(denom, 100))) + + shares, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, alice)) + require.NoError(t, err) + require.Equal(t, math.NewInt(100), shares) + + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.NoError(t, err) + require.Equal(t, math.NewInt(100), total) +} + +func TestMintShares_Subsequent(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // Seed: alice has 100 shares against balance 50 (consumed via fees) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(100))) + + // Bob deposits 100 when balance is 50 (PRE-deposit) and 150 (POST-deposit) + // The mint formula uses balance BEFORE the new deposit lands. + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 50)) + require.NoError(t, k.MintShares(ctx, consumerId, bob, sdk.NewInt64Coin(denom, 100))) + + // Bob: shares = 100 * 100 / 50 = 200 + shares, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, bob)) + require.NoError(t, err) + require.Equal(t, math.NewInt(200), shares) + + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.NoError(t, err) + require.Equal(t, math.NewInt(300), total) +} + +func TestMintShares_LazyInvalidation(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // Seed: alice has 100 shares, balance is 0 (pool fully consumed by fees) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(100))) + + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 0)) + require.NoError(t, k.MintShares(ctx, consumerId, bob, sdk.NewInt64Coin(denom, 50))) + + // Alice's shares should be wiped (lazy invalidation), Bob's recorded as initial + _, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, alice)) + require.ErrorIs(t, err, collections.ErrNotFound) + + bobShares, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, bob)) + require.NoError(t, err) + require.Equal(t, math.NewInt(50), bobShares) + + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.NoError(t, err) + require.Equal(t, math.NewInt(50), total) +} + +func TestMintShares_SubShareDeposit(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // Seed: alice has 1_000_000 shares of 1_000_000 total, balance is huge (1_000_000_000). + // Bob's tiny 1-unit deposit would mint floor(1 * 1_000_000 / 1_000_000_000) = 0 shares. + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(1_000_000))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(1_000_000))) + + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 1_000_000_000)) + err := k.MintShares(ctx, consumerId, bob, sdk.NewInt64Coin(denom, 1)) + require.ErrorIs(t, err, providertypes.ErrDepositTooSmall) + + // No state mutation: bob has no entry, total unchanged, alice unchanged. + _, err = k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, bob)) + require.ErrorIs(t, err, collections.ErrNotFound) + + aliceShares, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, alice)) + require.NoError(t, err) + require.Equal(t, math.NewInt(1_000_000), aliceShares) + + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.NoError(t, err) + require.Equal(t, math.NewInt(1_000_000), total) +} + +func TestWithdrawShares_Full(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // Alice sole depositor with 100 shares against balance 100 + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 100)) + + // Request 200 (over claim) — should burn all shares, return claim=100 + tokens, err := k.WithdrawShares(ctx, consumerId, alice, sdk.NewInt64Coin(denom, 200)) + require.NoError(t, err) + require.Equal(t, math.NewInt(100), tokens.Amount) + + _, err = k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, alice)) + require.ErrorIs(t, err, collections.ErrNotFound) + + _, err = k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.ErrorIs(t, err, collections.ErrNotFound) +} + +func TestWithdrawShares_Partial(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // Two depositors, balance 200, total 200 + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, bob), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(200))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 200)) + + // Alice withdraws 50 (partial, well below claim 100) + tokens, err := k.WithdrawShares(ctx, consumerId, alice, sdk.NewInt64Coin(denom, 50)) + require.NoError(t, err) + require.Equal(t, math.NewInt(50), tokens.Amount) + + // Alice burned 50 shares; total = 150; alice = 50 + aliceShares, _ := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, alice)) + require.Equal(t, math.NewInt(50), aliceShares) + total, _ := k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.Equal(t, math.NewInt(150), total) +} + +func TestWithdrawShares_Empty(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 0)) + + _, err := k.WithdrawShares(ctx, consumerId, alice, sdk.NewInt64Coin(denom, 50)) + require.ErrorIs(t, err, providertypes.ErrPoolEmpty) +} + +func TestWithdrawShares_SubShareGuard(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // Seed: alice has 100 shares of 100 total against a huge balance (1_000_000). + // Claim = 100 * 1_000_000 / 100 = 1_000_000. A tiny 1-unit withdrawal hits + // the partial branch and computes sharesToBurn = floor(1 * 100 / 1_000_000) = 0, + // which must trigger the sub-share guard. + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 1_000_000)) + + tokens, err := k.WithdrawShares(ctx, consumerId, alice, sdk.NewInt64Coin(denom, 1)) + require.ErrorIs(t, err, providertypes.ErrSubShareWithdraw) + require.Equal(t, sdk.Coin{}, tokens) + + // No state mutation: alice still has 100 shares, total still 100. + aliceShares, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, alice)) + require.NoError(t, err) + require.Equal(t, math.NewInt(100), aliceShares) + + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.NoError(t, err) + require.Equal(t, math.NewInt(100), total) +} + +func TestSweepConsumerFeePoolDenom(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // alice 30, bob 70, total 100, balance 100 — no dust + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(30))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, bob), math.NewInt(70))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(100))) + + providerModuleName := providertypes.ModuleName + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 100)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providerModuleName, sdk.NewCoins(sdk.NewInt64Coin(denom, 100))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providerModuleName, alice, sdk.NewCoins(sdk.NewInt64Coin(denom, 30))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providerModuleName, bob, sdk.NewCoins(sdk.NewInt64Coin(denom, 70))).Return(nil) + // No dust -> no FundCommunityPool call + + require.NoError(t, k.SweepConsumerFeePoolDenom(ctx, consumerId, denom)) + + // All share records and total cleared + _, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, alice)) + require.ErrorIs(t, err, collections.ErrNotFound) + _, err = k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, bob)) + require.ErrorIs(t, err, collections.ErrNotFound) + _, err = k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.ErrorIs(t, err, collections.ErrNotFound) +} + +func TestSweepConsumerFeePoolDenom_WithDust(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // alice 1, bob 2, total 3, balance 10 + // alice claim: floor(1*10/3) = 3; bob claim: floor(2*10/3) = 6; dust = 1 + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(1))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, bob), math.NewInt(2))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(3))) + + providerModuleName := providertypes.ModuleName + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 10)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providerModuleName, sdk.NewCoins(sdk.NewInt64Coin(denom, 10))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providerModuleName, alice, sdk.NewCoins(sdk.NewInt64Coin(denom, 3))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providerModuleName, bob, sdk.NewCoins(sdk.NewInt64Coin(denom, 6))).Return(nil) + mocks.MockDistributionKeeper.EXPECT().FundCommunityPool( + ctx, sdk.NewCoins(sdk.NewInt64Coin(denom, 1)), + authtypes.NewModuleAddress(providertypes.ModuleName)).Return(nil) + + require.NoError(t, k.SweepConsumerFeePoolDenom(ctx, consumerId, denom)) +} + +func TestSweepConsumerFeePoolDenom_DistrModuleRecipientUsesCommunityPool(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + distrAddr := authtypes.NewModuleAddress(disttypes.ModuleName) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, distrAddr), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(100))) + + providerModuleName := providertypes.ModuleName + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 100)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providerModuleName, sdk.NewCoins(sdk.NewInt64Coin(denom, 100))).Return(nil) + // Distribution module account share goes via FundCommunityPool, NOT raw bank send + mocks.MockDistributionKeeper.EXPECT().FundCommunityPool( + ctx, sdk.NewCoins(sdk.NewInt64Coin(denom, 100)), + authtypes.NewModuleAddress(providertypes.ModuleName)).Return(nil) + + require.NoError(t, k.SweepConsumerFeePoolDenom(ctx, consumerId, denom)) +} + +func TestSweepConsumerFeePoolDenom_AllFloorToZero(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + denom := "uphoton" + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // alice 1 share, bob 1 share, total 2, balance 1. + // alice slice: floor(1*1/2) = 0 -> skipped + // bob slice: floor(1*1/2) = 0 -> skipped + // distributed = 0; dust = 1 -> entire balance routed to community pool. + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, alice), math.NewInt(1))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, denom, bob), math.NewInt(1))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), math.NewInt(2))) + + providerModuleName := providertypes.ModuleName + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, denom).Return(sdk.NewInt64Coin(denom, 1)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providerModuleName, sdk.NewCoins(sdk.NewInt64Coin(denom, 1))).Return(nil) + // No per-holder SendCoinsFromModuleToAccount: every slice floors to zero. + mocks.MockDistributionKeeper.EXPECT().FundCommunityPool( + ctx, sdk.NewCoins(sdk.NewInt64Coin(denom, 1)), + authtypes.NewModuleAddress(providertypes.ModuleName)).Return(nil) + + require.NoError(t, k.SweepConsumerFeePoolDenom(ctx, consumerId, denom)) + + // All share records and total cleared. + _, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, alice)) + require.ErrorIs(t, err, collections.ErrNotFound) + _, err = k.ConsumerFeePoolShares.Get(ctx, collections.Join3(consumerId, denom, bob)) + require.ErrorIs(t, err, collections.ErrNotFound) + _, err = k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(consumerId, denom)) + require.ErrorIs(t, err, collections.ErrNotFound) +} + +func TestSweepConsumerFeePool_AllDenoms(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // alice has shares in two denoms + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", alice), math.NewInt(10))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(10))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uatone", alice), math.NewInt(5))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uatone"), math.NewInt(5))) + + mocks.MockBankKeeper.EXPECT().GetAllBalances(ctx, poolAddr).Return( + sdk.NewCoins(sdk.NewInt64Coin("uphoton", 10), sdk.NewInt64Coin("uatone", 5))) + + // Two per-denom sweeps. Expect bank ops for each. + for _, c := range []sdk.Coin{ + sdk.NewInt64Coin("uatone", 5), + sdk.NewInt64Coin("uphoton", 10), + } { + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, c.Denom).Return(c) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providertypes.ModuleName, sdk.NewCoins(c)).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, alice, sdk.NewCoins(c)).Return(nil) + } + + require.NoError(t, k.SweepConsumerFeePool(ctx, consumerId, nil)) +} diff --git a/x/vaas/provider/keeper/genesis.go b/x/vaas/provider/keeper/genesis.go index 473a2e7..abbb045 100644 --- a/x/vaas/provider/keeper/genesis.go +++ b/x/vaas/provider/keeper/genesis.go @@ -11,6 +11,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" "cosmossdk.io/collections" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -139,6 +140,78 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) []abc } } + // Load primary share records and accumulate per-(consumer, denom) totals + // in a single pass. GenesisState.Validate has already vetted the input. + totals := map[uint64]map[string]math.Int{} + for _, s := range genState.ConsumerFeePoolShares { + addr, err := sdk.AccAddressFromBech32(s.Depositor) + if err != nil { + panic(fmt.Errorf("invalid depositor in genesis: %w", err)) + } + if err := k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(s.ConsumerId, s.Denom, addr), s.Shares, + ); err != nil { + panic(err) + } + if _, ok := totals[s.ConsumerId]; !ok { + totals[s.ConsumerId] = map[string]math.Int{} + } + if cur, ok := totals[s.ConsumerId][s.Denom]; ok { + totals[s.ConsumerId][s.Denom] = cur.Add(s.Shares) + } else { + totals[s.ConsumerId][s.Denom] = s.Shares + } + } + for consumerId, byDenom := range totals { + for denom, sum := range byDenom { + if err := k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, denom), sum, + ); err != nil { + panic(err) + } + } + } + + // Rebuild reverse-lookup from non-DELETED consumers (read phase collection) + // and, in the same pass, check that no fee pool address holds a balance + // without a matching share record. Bank's InitGenesis runs before ours + // (see app.go OrderInitGenesis), so any pool balance present here was put + // there by the genesis file itself. Such a balance with no shares would + // be unreachable from runtime ops (only MintShares moves funds into a + // pool, and it always credits shares), so we treat it as a malformed + // genesis and halt rather than silently accept funds nobody can claim. + phaseIter, err := k.ConsumerPhase.Iterate(ctx, nil) + if err != nil { + panic(err) + } + defer phaseIter.Close() + for ; phaseIter.Valid(); phaseIter.Next() { + consumerId, err := phaseIter.Key() + if err != nil { + panic(err) + } + phaseRaw, err := phaseIter.Value() + if err != nil { + panic(err) + } + if types.ConsumerPhase(phaseRaw) == types.CONSUMER_PHASE_DELETED { + continue + } + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + if err := k.FeePoolAddressToConsumerId.Set(ctx, poolAddr, consumerId); err != nil { + panic(err) + } + denomsWithShares := totals[consumerId] + for _, coin := range k.bankKeeper.GetAllBalances(ctx, poolAddr) { + if _, ok := denomsWithShares[coin.Denom]; !ok { + panic(fmt.Errorf( + "fee-pool genesis invariant violated: consumer %d pool %s holds %s but has no shares for that denom", + consumerId, poolAddr.String(), coin.String(), + )) + } + } + } + k.SetParams(ctx, genState.Params) return k.InitGenesisValUpdates(ctx) } @@ -256,6 +329,16 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { k.GetAllConsumerAddrsToPrune(ctx, consumerId)...) } + // Export share records and accumulate per-(consumer, denom) totals in + // a single pass for the sanity check below. + feePoolShares, recomputedTotals := k.exportFeePoolShares(ctx) + + // Sanity check: compare stored ConsumerFeePoolTotalShares against the + // totals we just recomputed. Divergence is logged but not blocking — + // export should never fail on a sanity check (spec section "Genesis + // export/import"). + k.checkFeePoolTotalsConsistency(ctx, recomputedTotals) + // TODO (PERMISSIONLESS) return types.NewGenesisState( k.GetValidatorSetUpdateId(ctx), @@ -265,5 +348,100 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { k.GetAllValidatorConsumerPubKeys(ctx, nil), k.GetAllValidatorsByConsumerAddr(ctx, nil), consumerAddrsToPrune, + feePoolShares, ) } + +// exportFeePoolShares walks ConsumerFeePoolShares once, emitting both the +// flat list for genesis export and a per-(consumer, denom) recomputed-totals +// map for the export-time sanity check. +func (k Keeper) exportFeePoolShares( + ctx sdk.Context, +) ([]types.ConsumerFeePoolShare, map[uint64]map[string]math.Int) { + shares := []types.ConsumerFeePoolShare{} + totals := map[uint64]map[string]math.Int{} + iter, err := k.ConsumerFeePoolShares.Iterate(ctx, nil) + if err != nil { + k.Logger(ctx).Error("fee-pool shares: iterate failed at export", "error", err.Error()) + return shares, totals + } + defer iter.Close() + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + k.Logger(ctx).Error("fee-pool shares: key read failed at export", "error", err.Error()) + return shares, totals + } + val, err := iter.Value() + if err != nil { + k.Logger(ctx).Error("fee-pool shares: value read failed at export", "error", err.Error()) + return shares, totals + } + shares = append(shares, types.ConsumerFeePoolShare{ + ConsumerId: key.K1(), + Depositor: key.K3().String(), + Denom: key.K2(), + Shares: val, + }) + if _, ok := totals[key.K1()]; !ok { + totals[key.K1()] = map[string]math.Int{} + } + if cur, ok := totals[key.K1()][key.K2()]; ok { + totals[key.K1()][key.K2()] = cur.Add(val) + } else { + totals[key.K1()][key.K2()] = val + } + } + return shares, totals +} + +// checkFeePoolTotalsConsistency compares stored ConsumerFeePoolTotalShares +// against a recomputed total map and logs both directions of mismatch. +func (k Keeper) checkFeePoolTotalsConsistency( + ctx sdk.Context, recomputed map[uint64]map[string]math.Int, +) { + seen := map[uint64]map[string]bool{} + totIter, err := k.ConsumerFeePoolTotalShares.Iterate(ctx, nil) + if err != nil { + k.Logger(ctx).Error("fee-pool totals: iterate failed at export", "error", err.Error()) + return + } + defer totIter.Close() + for ; totIter.Valid(); totIter.Next() { + key, err := totIter.Key() + if err != nil { + k.Logger(ctx).Error("fee-pool totals: key read failed at export", "error", err.Error()) + return + } + val, err := totIter.Value() + if err != nil { + k.Logger(ctx).Error("fee-pool totals: value read failed at export", "error", err.Error()) + return + } + consumerId, denom := key.K1(), key.K2() + if _, ok := seen[consumerId]; !ok { + seen[consumerId] = map[string]bool{} + } + seen[consumerId][denom] = true + + expected, ok := recomputed[consumerId][denom] + if !ok || !expected.Equal(val) { + k.Logger(ctx).Error( + "fee-pool total_shares: stored != recomputed at export", + "consumer_id", consumerId, "denom", denom, + "stored", val.String(), + ) + } + } + // Reverse direction: recomputed total exists but no stored total. + for consumerId, byDenom := range recomputed { + for denom := range byDenom { + if !seen[consumerId][denom] { + k.Logger(ctx).Error( + "fee-pool total_shares: shares exist without stored total at export", + "consumer_id", consumerId, "denom", denom, + ) + } + } + } +} diff --git a/x/vaas/provider/keeper/genesis_test.go b/x/vaas/provider/keeper/genesis_test.go index 5114d75..a47f952 100644 --- a/x/vaas/provider/keeper/genesis_test.go +++ b/x/vaas/provider/keeper/genesis_test.go @@ -9,6 +9,9 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "cosmossdk.io/collections" + "cosmossdk.io/math" + clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -155,6 +158,12 @@ func TestInitGenesisRestoresPerConsumerStateAndDerivedQueues(t *testing.T) { // so we only expect GetBondedValidatorsByPower once. mocks.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower(gomock.Any()).Return([]stakingtypes.Validator{}, nil).Times(1) + // InitGenesis walks each non-DELETED consumer's pool address to check for + // orphan balances. No genesis fixture in this test puts any coins at those + // addresses, so the bank reports empty balances. + mocks.MockBankKeeper.EXPECT().GetAllBalances(gomock.Any(), gomock.Any()). + Return(sdk.NewCoins()).AnyTimes() + owner := sdk.AccAddress([]byte("vaas-test-owner-1234")).String() md := providertypes.ConsumerMetadata{Name: "n", Description: "d", Metadata: "m"} spawnAt := time.Unix(1_700_000_000, 0).UTC() @@ -374,6 +383,9 @@ func TestGenesisRoundTrip(t *testing.T) { // InitGenesisValUpdates reads from staking; mock it on both keepers. stakingA.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower(gomock.Any()).Return(nil, nil).AnyTimes() stakingB.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower(gomock.Any()).Return(nil, nil).AnyTimes() + // InitGenesis walks fee-pool addresses for an orphan-balance check. + stakingB.MockBankKeeper.EXPECT().GetAllBalances(gomock.Any(), gomock.Any()). + Return(sdk.NewCoins()).AnyTimes() _ = pkB.InitGenesis(ctxB, expA) @@ -403,3 +415,80 @@ func TestGenesisRoundTrip(t *testing.T) { require.Equal(t, expA, expB, "round-trip must be a fixed point") } + +func TestExportGenesis_IncludesShares(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + k.SetParams(ctx, providertypes.DefaultParams()) + + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(uint64(0), "uphoton", alice), math.NewInt(60))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(uint64(0), "uphoton", bob), math.NewInt(40))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(uint64(0), "uphoton"), math.NewInt(100))) + + gs := k.ExportGenesis(ctx) + require.Len(t, gs.ConsumerFeePoolShares, 2) + found := map[string]math.Int{} + for _, s := range gs.ConsumerFeePoolShares { + found[s.Depositor] = s.Shares + } + require.Equal(t, math.NewInt(60), found[alice.String()]) + require.Equal(t, math.NewInt(40), found[bob.String()]) +} + +func TestInitGenesis_RebuildsDerivedCollections(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // InitGenesisValUpdates queries the staking keeper for the validator set. + mocks.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower(gomock.Any()).Return(nil, nil).Times(1) + // InitGenesis walks fee-pool addresses for an orphan-balance check. + mocks.MockBankKeeper.EXPECT().GetAllBalances(gomock.Any(), gomock.Any()). + Return(sdk.NewCoins()).AnyTimes() + + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + gs := &providertypes.GenesisState{ + Params: providertypes.DefaultParams(), + ConsumerStates: []providertypes.ConsumerState{ + { + ConsumerId: 0, + ChainId: "chain-a", + Phase: providertypes.CONSUMER_PHASE_LAUNCHED, + ConsumerGenesis: *vaastypes.DefaultConsumerGenesisState(), + }, + { + ConsumerId: 1, + ChainId: "chain-b", + Phase: providertypes.CONSUMER_PHASE_DELETED, + ConsumerGenesis: *vaastypes.DefaultConsumerGenesisState(), + }, + }, + ConsumerFeePoolShares: []providertypes.ConsumerFeePoolShare{ + {ConsumerId: 0, Depositor: alice.String(), Denom: "uphoton", Shares: math.NewInt(60)}, + {ConsumerId: 0, Depositor: bob.String(), Denom: "uphoton", Shares: math.NewInt(40)}, + }, + } + + k.InitGenesis(ctx, gs) + + s, err := k.ConsumerFeePoolShares.Get(ctx, collections.Join3(uint64(0), "uphoton", alice)) + require.NoError(t, err) + require.Equal(t, math.NewInt(60), s) + + total, err := k.ConsumerFeePoolTotalShares.Get(ctx, collections.Join(uint64(0), "uphoton")) + require.NoError(t, err) + require.Equal(t, math.NewInt(100), total) + + cid, err := k.FeePoolAddressToConsumerId.Get(ctx, k.GetConsumerFeePoolAddress(0)) + require.NoError(t, err) + require.Equal(t, uint64(0), cid) + + _, err = k.FeePoolAddressToConsumerId.Get(ctx, k.GetConsumerFeePoolAddress(1)) + require.ErrorIs(t, err, collections.ErrNotFound) +} diff --git a/x/vaas/provider/keeper/grpc_query.go b/x/vaas/provider/keeper/grpc_query.go index 98a55d9..9517563 100644 --- a/x/vaas/provider/keeper/grpc_query.go +++ b/x/vaas/provider/keeper/grpc_query.go @@ -12,10 +12,14 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "cosmossdk.io/collections" errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" ) var _ types.QueryServer = Keeper{} @@ -321,6 +325,218 @@ func (k Keeper) QueryConsumerChain(goCtx context.Context, req *types.QueryConsum }, nil } +func (k Keeper) ConsumerFeePoolClaim( + goCtx context.Context, req *types.QueryConsumerFeePoolClaimRequest, +) (*types.QueryConsumerFeePoolClaimResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + exists, err := k.ConsumerPhase.Has(ctx, req.ConsumerId) + if err != nil { + return nil, status.Errorf(codes.Internal, "phase lookup: %s", err) + } + if !exists { + return nil, status.Errorf(codes.NotFound, "consumer %d does not exist", req.ConsumerId) + } + if k.GetConsumerPhase(ctx, req.ConsumerId) == types.CONSUMER_PHASE_DELETED { + return nil, status.Errorf(codes.NotFound, "consumer %d is deleted", req.ConsumerId) + } + + depositor, err := sdk.AccAddressFromBech32(req.Depositor) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid depositor: %s", err) + } + if k.isGovAuthority(depositor) { + depositor = authtypes.NewModuleAddress(disttypes.ModuleName) + } + + poolAddr := k.GetConsumerFeePoolAddress(req.ConsumerId) + coins := sdk.NewCoins() + prefix := collections.NewPrefixedPairRange[uint64, string](req.ConsumerId) + iter, err := k.ConsumerFeePoolTotalShares.Iterate(ctx, prefix) + if err != nil { + return nil, status.Errorf(codes.Internal, "iterate totals: %s", err) + } + defer iter.Close() + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + return nil, status.Errorf(codes.Internal, "key: %s", err) + } + total, err := iter.Value() + if err != nil { + return nil, status.Errorf(codes.Internal, "value: %s", err) + } + denom := key.K2() + if total.IsZero() { + continue + } + shares, err := k.ConsumerFeePoolShares.Get(ctx, + collections.Join3(req.ConsumerId, denom, depositor)) + if err != nil { + // No shares for this (consumer, depositor, denom) — skip. + continue + } + balance := k.bankKeeper.GetBalance(ctx, poolAddr, denom) + if balance.Amount.IsZero() { + continue + } + claim := shares.Mul(balance.Amount).Quo(total) + if claim.IsPositive() { + coins = coins.Add(sdk.NewCoin(denom, claim)) + } + } + return &types.QueryConsumerFeePoolClaimResponse{Claim: coins}, nil +} + +func (k Keeper) ConsumerFeePoolClaims( + goCtx context.Context, req *types.QueryConsumerFeePoolClaimsRequest, +) (*types.QueryConsumerFeePoolClaimsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + exists, err := k.ConsumerPhase.Has(ctx, req.ConsumerId) + if err != nil { + return nil, status.Errorf(codes.Internal, "phase lookup: %s", err) + } + if !exists { + return nil, status.Errorf(codes.NotFound, "consumer %d does not exist", req.ConsumerId) + } + if k.GetConsumerPhase(ctx, req.ConsumerId) == types.CONSUMER_PHASE_DELETED { + return nil, status.Errorf(codes.NotFound, "consumer %d is deleted", req.ConsumerId) + } + + type acc struct { + addr sdk.AccAddress + coins sdk.Coins + } + perDepositor := map[string]*acc{} + + // Cache per-denom (balance, total) so we don't pay one bank read + + // one collection read per (depositor, denom) tuple. + poolAddr := k.GetConsumerFeePoolAddress(req.ConsumerId) + balances := map[string]math.Int{} + totals := map[string]math.Int{} + + prefix := collections.NewPrefixedTripleRange[uint64, string, sdk.AccAddress](req.ConsumerId) + iter, err := k.ConsumerFeePoolShares.Iterate(ctx, prefix) + if err != nil { + return nil, status.Errorf(codes.Internal, "%s", err) + } + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + iter.Close() + return nil, status.Errorf(codes.Internal, "%s", err) + } + shares, err := iter.Value() + if err != nil { + iter.Close() + return nil, status.Errorf(codes.Internal, "%s", err) + } + denom := key.K2() + addr := key.K3() + + balance, ok := balances[denom] + if !ok { + balance = k.bankKeeper.GetBalance(ctx, poolAddr, denom).Amount + balances[denom] = balance + } + if balance.IsZero() { + continue + } + total, ok := totals[denom] + if !ok { + t, err := k.ConsumerFeePoolTotalShares.Get(ctx, + collections.Join(req.ConsumerId, denom)) + if err != nil { + continue + } + total = t + totals[denom] = total + } + if total.IsZero() { + continue + } + claim := shares.Mul(balance).Quo(total) + if claim.IsZero() { + continue + } + if entry, ok := perDepositor[addr.String()]; ok { + entry.coins = entry.coins.Add(sdk.NewCoin(denom, claim)) + } else { + perDepositor[addr.String()] = &acc{ + addr: addr, + coins: sdk.NewCoins(sdk.NewCoin(denom, claim)), + } + } + } + iter.Close() + + keys := make([]string, 0, len(perDepositor)) + for key := range perDepositor { + keys = append(keys, key) + } + sort.Strings(keys) + + total := uint64(len(keys)) + + // Decode pagination request. Key takes precedence over Offset: cosmos + // paging clients walk a result set with NextKey, so a non-nil Key means + // "resume from this depositor". + var offset, limit uint64 = 0, query.DefaultLimit + countTotal := true + if req.Pagination != nil { + if req.Pagination.Limit > 0 { + limit = req.Pagination.Limit + } + countTotal = req.Pagination.CountTotal + if len(req.Pagination.Key) > 0 { + // Find the first depositor >= Key. + cursor := string(req.Pagination.Key) + idx := sort.SearchStrings(keys, cursor) + offset = uint64(idx) + } else { + offset = req.Pagination.Offset + } + } + if offset > total { + offset = total + } + end := offset + limit + if end > total { + end = total + } + + claims := make([]types.DepositorClaim, 0, end-offset) + for _, key := range keys[offset:end] { + entry := perDepositor[key] + claims = append(claims, types.DepositorClaim{ + Depositor: entry.addr.String(), + Claim: entry.coins, + }) + } + + resp := &types.QueryConsumerFeePoolClaimsResponse{ + Claims: claims, + Pagination: &query.PageResponse{}, + } + if countTotal { + resp.Pagination.Total = total + } + if end < total { + // NextKey is the bech32 depositor at the boundary, encoded as bytes + // for the PageResponse contract. Resume decodes it back via + // string(req.Pagination.Key) and binary-searches the same sort order. + resp.Pagination.NextKey = []byte(keys[end]) + } + return resp, nil +} + func (k Keeper) QueryConsumerGenesisTime(goCtx context.Context, req *types.QueryConsumerGenesisTimeRequest) (*types.QueryConsumerGenesisTimeResponse, error) { if req == nil { return nil, status.Errorf(codes.InvalidArgument, "empty request") diff --git a/x/vaas/provider/keeper/grpc_query_test.go b/x/vaas/provider/keeper/grpc_query_test.go index 49b85e5..ff9e691 100644 --- a/x/vaas/provider/keeper/grpc_query_test.go +++ b/x/vaas/provider/keeper/grpc_query_test.go @@ -4,9 +4,16 @@ import ( "testing" "time" + "cosmossdk.io/collections" "cosmossdk.io/math" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" testkeeper "github.com/allinbits/vaas/testutil/keeper" providertypes "github.com/allinbits/vaas/x/vaas/provider/types" @@ -37,3 +44,104 @@ func TestQueryConsumerChainIncludesFeePoolAddress(t *testing.T) { require.NoError(t, err) require.Equal(t, expected, chain.FeePoolAddress) } + +func TestQueryConsumerFeePoolClaim(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", alice), math.NewInt(50))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 200)) + + res, err := k.ConsumerFeePoolClaim(ctx, &providertypes.QueryConsumerFeePoolClaimRequest{ + ConsumerId: consumerId, Depositor: alice.String(), + }) + require.NoError(t, err) + require.Equal(t, "100uphoton", res.Claim.String()) +} + +func TestQueryConsumerFeePoolClaim_UnknownConsumer(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + alice := sdk.AccAddress([]byte("alice___________")) + _, err := k.ConsumerFeePoolClaim(ctx, &providertypes.QueryConsumerFeePoolClaimRequest{ + ConsumerId: 999, Depositor: alice.String(), + }) + require.Error(t, err) + require.Equal(t, codes.NotFound, status.Code(err)) +} + +func TestQueryConsumerFeePoolClaim_GovAlias(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + distrAddr := authtypes.NewModuleAddress(disttypes.ModuleName) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", distrAddr), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 50)) + + res, err := k.ConsumerFeePoolClaim(ctx, &providertypes.QueryConsumerFeePoolClaimRequest{ + ConsumerId: consumerId, Depositor: k.GetAuthority(), + }) + require.NoError(t, err) + require.Equal(t, "50uphoton", res.Claim.String()) +} + +func TestQueryConsumerFeePoolClaims(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + alice := sdk.AccAddress([]byte("alice___________")) + bob := sdk.AccAddress([]byte("bob_____________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", alice), math.NewInt(30))) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", bob), math.NewInt(70))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 100)).AnyTimes() + + res, err := k.ConsumerFeePoolClaims(ctx, &providertypes.QueryConsumerFeePoolClaimsRequest{ + ConsumerId: consumerId, + }) + require.NoError(t, err) + require.Len(t, res.Claims, 2) + + // Strong assertions: alice 30 shares × 100 / 100 = 30; bob 70 × 100 / 100 = 70. + got := map[string]string{} + for _, c := range res.Claims { + got[c.Depositor] = c.Claim.String() + } + require.Equal(t, "30uphoton", got[alice.String()]) + require.Equal(t, "70uphoton", got[bob.String()]) +} + +func TestQueryConsumerFeePoolClaims_UnknownConsumer(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + _, err := k.ConsumerFeePoolClaims(ctx, &providertypes.QueryConsumerFeePoolClaimsRequest{ + ConsumerId: 999, + }) + require.Error(t, err) + require.Equal(t, codes.NotFound, status.Code(err)) +} diff --git a/x/vaas/provider/keeper/invariants.go b/x/vaas/provider/keeper/invariants.go index dada814..a515228 100644 --- a/x/vaas/provider/keeper/invariants.go +++ b/x/vaas/provider/keeper/invariants.go @@ -2,9 +2,12 @@ package keeper import ( "fmt" + "strings" types "github.com/allinbits/vaas/x/vaas/provider/types" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -16,6 +19,9 @@ func RegisterInvariants(ir sdk.InvariantRegistry, k *Keeper) { ir.RegisterRoute(types.ModuleName, "staking-keeper-equivalence", StakingKeeperEquivalenceInvariant(*k)) + + ir.RegisterRoute(types.ModuleName, "fee-pool-shares-consistency", + FeePoolSharesConsistencyInvariant(*k)) } // MaxProviderConsensusValidatorsInvariant checks that the number of provider consensus validators @@ -147,3 +153,150 @@ func StakingKeeperEquivalenceInvariant(k Keeper) sdk.Invariant { return "", false } } + +// FeePoolSharesConsistencyInvariant checks the per-consumer fee-pool share +// accounting against itself and against bank balances. For every +// (consumer, denom) pair it asserts: +// +// - sum(ConsumerFeePoolShares[(consumer, denom, *)]) +// == ConsumerFeePoolTotalShares[(consumer, denom)] +// - shares exist iff a total entry exists (no orphan rows on either side) +// - total_shares > 0 implies bank balance > 0 at the pool address +// (lazy-invalidation invariant: balance 0 with shares is reconciled on +// the next MintShares, but must not be allowed to persist post-block +// as a steady state) +// - bank balance > 0 implies total_shares > 0 (orphan-balance invariant: +// enforced at genesis and at every MintShares/Sweep call site) +func FeePoolSharesConsistencyInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var msgs []string + + // Pass 1: sum shares per (consumer, denom). + sums := map[uint64]map[string]math.Int{} + shareIter, err := k.ConsumerFeePoolShares.Iterate(ctx, nil) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("iterate shares: %v", err)), true + } + for ; shareIter.Valid(); shareIter.Next() { + key, err := shareIter.Key() + if err != nil { + shareIter.Close() + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("shares key: %v", err)), true + } + val, err := shareIter.Value() + if err != nil { + shareIter.Close() + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("shares value: %v", err)), true + } + cid, denom := key.K1(), key.K2() + if _, ok := sums[cid]; !ok { + sums[cid] = map[string]math.Int{} + } + if cur, ok := sums[cid][denom]; ok { + sums[cid][denom] = cur.Add(val) + } else { + sums[cid][denom] = val + } + } + shareIter.Close() + + // Pass 2: compare against stored totals; also catch orphan totals. + seen := map[uint64]map[string]bool{} + totalIter, err := k.ConsumerFeePoolTotalShares.Iterate(ctx, nil) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("iterate totals: %v", err)), true + } + for ; totalIter.Valid(); totalIter.Next() { + key, err := totalIter.Key() + if err != nil { + totalIter.Close() + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("totals key: %v", err)), true + } + total, err := totalIter.Value() + if err != nil { + totalIter.Close() + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("totals value: %v", err)), true + } + cid, denom := key.K1(), key.K2() + if _, ok := seen[cid]; !ok { + seen[cid] = map[string]bool{} + } + seen[cid][denom] = true + + expected, ok := sums[cid][denom] + if !ok { + msgs = append(msgs, fmt.Sprintf( + "total %s for (%d, %s) has no share records", total, cid, denom)) + continue + } + if !expected.Equal(total) { + msgs = append(msgs, fmt.Sprintf( + "sum(shares)=%s != total=%s for (%d, %s)", expected, total, cid, denom)) + } + + // Lazy-invalidation invariant: total > 0 implies pool balance > 0. + if total.IsPositive() { + bal := k.bankKeeper.GetBalance(ctx, k.GetConsumerFeePoolAddress(cid), denom) + if !bal.Amount.IsPositive() { + msgs = append(msgs, fmt.Sprintf( + "total %s for (%d, %s) but pool balance is zero", total, cid, denom)) + } + } + } + totalIter.Close() + + // Orphan shares (shares row with no totals row). + for cid, byDenom := range sums { + for denom := range byDenom { + if !seen[cid][denom] { + msgs = append(msgs, fmt.Sprintf( + "shares for (%d, %s) without stored total", cid, denom)) + } + } + } + + // Orphan-balance invariant: every non-zero pool balance must have shares. + // Walk FeePoolAddressToConsumerId so we only touch addresses we own. + addrIter, err := k.FeePoolAddressToConsumerId.Iterate(ctx, nil) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("iterate fee pool addrs: %v", err)), true + } + for ; addrIter.Valid(); addrIter.Next() { + addr, err := addrIter.Key() + if err != nil { + addrIter.Close() + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("addr key: %v", err)), true + } + cid, err := addrIter.Value() + if err != nil { + addrIter.Close() + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + fmt.Sprintf("addr value: %v", err)), true + } + for _, coin := range k.bankKeeper.GetAllBalances(ctx, addr) { + if !coin.Amount.IsPositive() { + continue + } + if !seen[cid][coin.Denom] { + msgs = append(msgs, fmt.Sprintf( + "pool (%d, %s) holds %s with no shares", cid, coin.Denom, coin.Amount)) + } + } + } + addrIter.Close() + + if len(msgs) > 0 { + return sdk.FormatInvariant(types.ModuleName, "fee-pool-shares-consistency", + strings.Join(msgs, "; ")), true + } + return "", false + } +} diff --git a/x/vaas/provider/keeper/keeper.go b/x/vaas/provider/keeper/keeper.go index a3db49e..434a6e4 100644 --- a/x/vaas/provider/keeper/keeper.go +++ b/x/vaas/provider/keeper/keeper.go @@ -17,6 +17,7 @@ import ( addresscodec "cosmossdk.io/core/address" corestoretypes "cosmossdk.io/core/store" "cosmossdk.io/log" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -31,15 +32,16 @@ type Keeper struct { storeService corestoretypes.KVStoreService - cdc codec.BinaryCodec - accountKeeper vaastypes.AccountKeeper - clientKeeper vaastypes.ClientKeeper - clientV2Keeper vaastypes.ClientV2Keeper - stakingKeeper vaastypes.StakingKeeper - slashingKeeper vaastypes.SlashingKeeper - bankKeeper vaastypes.BankKeeper - govKeeper govkeeper.Keeper - feeCollectorName string + cdc codec.BinaryCodec + accountKeeper vaastypes.AccountKeeper + clientKeeper vaastypes.ClientKeeper + clientV2Keeper vaastypes.ClientV2Keeper + stakingKeeper vaastypes.StakingKeeper + slashingKeeper vaastypes.SlashingKeeper + bankKeeper vaastypes.BankKeeper + distributionKeeper vaastypes.DistributionKeeper + govKeeper govkeeper.Keeper + feeCollectorName string validatorAddressCodec addresscodec.Codec consensusAddressCodec addresscodec.Codec @@ -76,6 +78,16 @@ type Keeper struct { // Validator set collections ConsumerValidators collections.Map[collections.Pair[uint64, []byte], types.ConsensusValidator] LastProviderConsensusVals collections.Map[[]byte, types.ConsensusValidator] + + // Fee pool collections. + // + // ConsumerFeePoolShares is keyed (consumer_id, denom, depositor): denom + // is the second component so that per-denom operations (sweep, claim, + // invalidation) can prefix-iterate a single (consumer_id, denom) range + // without scanning across the full depositor set for the consumer. + ConsumerFeePoolShares collections.Map[collections.Triple[uint64, string, sdk.AccAddress], math.Int] + ConsumerFeePoolTotalShares collections.Map[collections.Pair[uint64, string], math.Int] + FeePoolAddressToConsumerId collections.Map[sdk.AccAddress, uint64] } // NewKeeper creates a new provider Keeper instance @@ -86,6 +98,7 @@ func NewKeeper( stakingKeeper vaastypes.StakingKeeper, slashingKeeper vaastypes.SlashingKeeper, accountKeeper vaastypes.AccountKeeper, bankKeeper vaastypes.BankKeeper, + distributionKeeper vaastypes.DistributionKeeper, govKeeper govkeeper.Keeper, authority string, validatorAddressCodec, consensusAddressCodec addresscodec.Codec, @@ -103,6 +116,7 @@ func NewKeeper( slashingKeeper: slashingKeeper, accountKeeper: accountKeeper, bankKeeper: bankKeeper, + distributionKeeper: distributionKeeper, feeCollectorName: feeCollectorName, validatorAddressCodec: validatorAddressCodec, consensusAddressCodec: consensusAddressCodec, @@ -156,6 +170,26 @@ func NewKeeper( LastProviderConsensusVals: collections.NewMap(sb, types.LastProviderConsensusVals, "last_provider_consensus_vals", collections.BytesKey, codec.CollValue[types.ConsensusValidator](cdc)), } + // Fee pool collections + k.ConsumerFeePoolShares = collections.NewMap( + sb, types.ConsumerFeePoolSharesKeyPrefix, + types.ConsumerFeePoolSharesKeyName, + collections.TripleKeyCodec(collections.Uint64Key, collections.StringKey, sdk.AccAddressKey), + sdk.IntValue, + ) + k.ConsumerFeePoolTotalShares = collections.NewMap( + sb, types.ConsumerFeePoolTotalSharesKeyPrefix, + types.ConsumerFeePoolTotalSharesKeyName, + collections.PairKeyCodec(collections.Uint64Key, collections.StringKey), + sdk.IntValue, + ) + k.FeePoolAddressToConsumerId = collections.NewMap( + sb, types.FeePoolAddressToConsumerIdKeyPrefix, + types.FeePoolAddressToConsumerIdKeyName, + sdk.AccAddressKey, + collections.Uint64Value, + ) + schema, err := sb.Build() if err != nil { panic(err) @@ -175,6 +209,17 @@ func (k Keeper) GetAuthority() string { return k.authority } +// isGovAuthority reports whether addr is the gov module authority. The check +// is byte-wise rather than string-wise so that valid-but-non-canonical bech32 +// encodings (e.g. uppercase) of the same address still match. +func (k Keeper) isGovAuthority(addr sdk.AccAddress) bool { + authAddr, err := sdk.AccAddressFromBech32(k.authority) + if err != nil { + return false + } + return addr.Equals(authAddr) +} + // ValidatorAddressCodec returns the app validator address codec. func (k Keeper) ValidatorAddressCodec() addresscodec.Codec { return k.validatorAddressCodec diff --git a/x/vaas/provider/keeper/msg_server.go b/x/vaas/provider/keeper/msg_server.go index e844ad8..2508c44 100644 --- a/x/vaas/provider/keeper/msg_server.go +++ b/x/vaas/provider/keeper/msg_server.go @@ -15,6 +15,8 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -257,6 +259,13 @@ func (k msgServer) CreateConsumer(goCtx context.Context, msg *types.MsgCreateCon ) resp.ConsumerId = consumerId + + if err := k.FeePoolAddressToConsumerId.Set(ctx, + k.GetConsumerFeePoolAddress(consumerId), consumerId, + ); err != nil { + return &resp, err + } + return &resp, nil } @@ -466,3 +475,195 @@ func (k msgServer) RemoveConsumer(goCtx context.Context, msg *types.MsgRemoveCon return &resp, err } + +// FundConsumerFeePool deposits funds into a consumer's fee pool. +func (k msgServer) FundConsumerFeePool( + goCtx context.Context, msg *types.MsgFundConsumerFeePool, +) (*types.MsgFundConsumerFeePoolResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + exists, err := k.ConsumerPhase.Has(ctx, msg.ConsumerId) + if err != nil { + return nil, err + } + if !exists { + return nil, errorsmod.Wrapf(types.ErrUnknownConsumerId, + "consumer %d does not exist", msg.ConsumerId) + } + if k.GetConsumerPhase(ctx, msg.ConsumerId) == types.CONSUMER_PHASE_DELETED { + return nil, errorsmod.Wrapf(types.ErrInvalidPhase, + "consumer %d is deleted", msg.ConsumerId) + } + + params := k.GetParams(ctx) + if msg.Amount.Denom != params.FeesPerBlock.Denom { + return nil, errorsmod.Wrapf(types.ErrInvalidFundDenom, + "expected denom %s, got %s", params.FeesPerBlock.Denom, msg.Amount.Denom) + } + + poolAddr := k.GetConsumerFeePoolAddress(msg.ConsumerId) + coins := sdk.NewCoins(msg.Amount) + + signerAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + isGov := k.isGovAuthority(signerAddr) + + var depositor sdk.AccAddress + if isGov { + depositor = authtypes.NewModuleAddress(disttypes.ModuleName) + } else { + if err := k.bankKeeper.SendCoinsFromAccountToModule( + ctx, signerAddr, types.ModuleName, coins, + ); err != nil { + return nil, err + } + depositor = signerAddr + } + + // MintShares must run before the funds land in the pool, because the + // share math reads the pool balance as "balance before this deposit". + if err := k.MintShares(ctx, msg.ConsumerId, depositor, msg.Amount); err != nil { + return nil, err + } + + if isGov { + if err := k.distributionKeeper.DistributeFromFeePool(ctx, coins, poolAddr); err != nil { + return nil, err + } + } else { + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, types.ModuleName, poolAddr, coins, + ); err != nil { + return nil, err + } + } + + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeConsumerFeePoolFund, + sdk.NewAttribute(types.AttributeConsumerId, strconv.FormatUint(msg.ConsumerId, 10)), + sdk.NewAttribute(types.AttributeDepositor, depositor.String()), + sdk.NewAttribute(types.AttributeAmount, msg.Amount.String()), + )) + return &types.MsgFundConsumerFeePoolResponse{}, nil +} + +// WithdrawConsumerFeePool burns the depositor's shares across one or more +// denoms and returns the corresponding tokens. The handler is all-or-nothing +// at the tx boundary: a mid-flight failure on any denom returns an error and +// the SDK rolls back the entire tx. +// +// When the signer is the gov authority, the depositor identity is the +// distribution module account and the tokens are routed back to the community +// pool rather than to the signer. +func (k msgServer) WithdrawConsumerFeePool( + goCtx context.Context, msg *types.MsgWithdrawConsumerFeePool, +) (*types.MsgWithdrawConsumerFeePoolResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + signerAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + isGov := k.isGovAuthority(signerAddr) + + var depositor sdk.AccAddress + if isGov { + depositor = authtypes.NewModuleAddress(disttypes.ModuleName) + } else { + depositor = signerAddr + } + + delivered := sdk.NewCoins() + for _, amt := range msg.Amount { + tokens, err := k.WithdrawShares(ctx, msg.ConsumerId, depositor, amt) + if err != nil { + return nil, err + } + delivered = delivered.Add(tokens) + } + + if !delivered.IsZero() { + poolAddr := k.GetConsumerFeePoolAddress(msg.ConsumerId) + providerAddr := authtypes.NewModuleAddress(types.ModuleName) + if err := k.bankKeeper.SendCoinsFromAccountToModule( + ctx, poolAddr, types.ModuleName, delivered, + ); err != nil { + return nil, err + } + if isGov { + if err := k.distributionKeeper.FundCommunityPool(ctx, delivered, providerAddr); err != nil { + return nil, err + } + } else { + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, types.ModuleName, depositor, delivered, + ); err != nil { + return nil, err + } + } + } + + // On the gov path, depositor and recipient are both the distribution + // module account, so the recipient attribute alone can't tell apart a + // regular withdraw from a gov clawback to the community pool. The + // withdraw_path attribute disambiguates for indexers. + withdrawPath := types.WithdrawPathDirect + if isGov { + withdrawPath = types.WithdrawPathCommunityPool + } + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeConsumerFeePoolWithdraw, + sdk.NewAttribute(types.AttributeConsumerId, strconv.FormatUint(msg.ConsumerId, 10)), + sdk.NewAttribute(types.AttributeDepositor, depositor.String()), + sdk.NewAttribute(types.AttributeRecipient, depositor.String()), + sdk.NewAttribute(types.AttributeAmount, delivered.String()), + sdk.NewAttribute(types.AttributeWithdrawPath, withdrawPath), + )) + return &types.MsgWithdrawConsumerFeePoolResponse{Amount: delivered}, nil +} + +func (k msgServer) SweepConsumerFeePool( + goCtx context.Context, msg *types.MsgSweepConsumerFeePool, +) (*types.MsgSweepConsumerFeePoolResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + exists, err := k.ConsumerPhase.Has(ctx, msg.ConsumerId) + if err != nil { + return nil, err + } + if !exists { + return nil, errorsmod.Wrapf(types.ErrUnknownConsumerId, + "consumer %d does not exist", msg.ConsumerId) + } + if k.GetConsumerPhase(ctx, msg.ConsumerId) == types.CONSUMER_PHASE_DELETED { + return nil, errorsmod.Wrapf(types.ErrInvalidPhase, + "consumer %d is deleted; pool already auto-swept on delete", msg.ConsumerId) + } + + ownerAddrString, err := k.GetConsumerOwnerAddress(ctx, msg.ConsumerId) + if err != nil { + return nil, errorsmod.Wrapf(types.ErrNoOwnerAddress, + "consumer %d has no owner: %s", msg.ConsumerId, err) + } + signerAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + ownerAddr, err := sdk.AccAddressFromBech32(ownerAddrString) + if err != nil { + return nil, errorsmod.Wrapf(types.ErrNoOwnerAddress, + "stored owner address %s is invalid: %s", ownerAddrString, err) + } + if !signerAddr.Equals(ownerAddr) { + return nil, errorsmod.Wrapf(types.ErrUnauthorized, + "only consumer owner %s may sweep, got %s", ownerAddrString, msg.Signer) + } + + // k.SweepConsumerFeePool here would recurse; call the embedded Keeper's. + if err := k.Keeper.SweepConsumerFeePool(ctx, msg.ConsumerId, msg.Denoms); err != nil { + return nil, errorsmod.Wrap(types.ErrFeePoolSweepFailed, err.Error()) + } + return &types.MsgSweepConsumerFeePoolResponse{}, nil +} diff --git a/x/vaas/provider/keeper/msg_server_test.go b/x/vaas/provider/keeper/msg_server_test.go index 9b610fb..737bb63 100644 --- a/x/vaas/provider/keeper/msg_server_test.go +++ b/x/vaas/provider/keeper/msg_server_test.go @@ -5,7 +5,13 @@ import ( "github.com/stretchr/testify/require" + "cosmossdk.io/collections" + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/codec/address" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" testkeeper "github.com/allinbits/vaas/testutil/keeper" providerkeeper "github.com/allinbits/vaas/x/vaas/provider/keeper" @@ -61,6 +67,24 @@ func TestCreateConsumer(t *testing.T) { require.Equal(t, providertypes.CONSUMER_PHASE_REGISTERED, phase) } +func TestCreateConsumer_PopulatesReverseLookup(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + resp, err := ms.CreateConsumer(ctx, &providertypes.MsgCreateConsumer{ + Submitter: "submitter", ChainId: "chainId", + Metadata: providertypes.ConsumerMetadata{Name: "n", Description: "d"}, + InitializationParameters: &providertypes.ConsumerInitializationParameters{}, + }) + require.NoError(t, err) + + poolAddr := k.GetConsumerFeePoolAddress(resp.ConsumerId) + consumerId, err := k.FeePoolAddressToConsumerId.Get(ctx, poolAddr) + require.NoError(t, err) + require.Equal(t, resp.ConsumerId, consumerId) +} + func TestCreateConsumerDuplicateChainId(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() @@ -209,3 +233,271 @@ func TestUpdateConsumerDuplicateChainId(t *testing.T) { require.NoError(t, err) require.Equal(t, chainId2, actualChainId) } + +func TestFundConsumerFeePool_RegularSigner(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_REGISTERED) + k.SetParams(ctx, providertypes.DefaultParams()) + params := k.GetParams(ctx) + params.FeesPerBlock = sdk.NewInt64Coin("uphoton", 10) + k.SetParams(ctx, params) + + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + amount := sdk.NewInt64Coin("uphoton", 100) + + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 0)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, alice, providertypes.ModuleName, sdk.NewCoins(amount)).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, poolAddr, sdk.NewCoins(amount)).Return(nil) + + _, err := ms.FundConsumerFeePool(ctx, &providertypes.MsgFundConsumerFeePool{ + Signer: alice.String(), ConsumerId: consumerId, Amount: amount, + }) + require.NoError(t, err) + + shares, _ := k.ConsumerFeePoolShares.Get(ctx, + collections.Join3(consumerId, "uphoton", alice)) + require.Equal(t, math.NewInt(100), shares) +} + +func TestFundConsumerFeePool_GovAuthority(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_REGISTERED) + k.SetParams(ctx, providertypes.DefaultParams()) + params := k.GetParams(ctx) + params.FeesPerBlock = sdk.NewInt64Coin("uphoton", 10) + k.SetParams(ctx, params) + + govAddr := k.GetAuthority() + distrAddr := authtypes.NewModuleAddress(disttypes.ModuleName) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + amount := sdk.NewInt64Coin("uphoton", 1000) + + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 0)) + mocks.MockDistributionKeeper.EXPECT().DistributeFromFeePool( + ctx, sdk.NewCoins(amount), poolAddr).Return(nil) + + _, err := ms.FundConsumerFeePool(ctx, &providertypes.MsgFundConsumerFeePool{ + Signer: govAddr, ConsumerId: consumerId, Amount: amount, + }) + require.NoError(t, err) + + shares, _ := k.ConsumerFeePoolShares.Get(ctx, + collections.Join3(consumerId, "uphoton", distrAddr)) + require.Equal(t, math.NewInt(1000), shares) +} + +func TestFundConsumerFeePool_RejectsUnknownConsumer(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + alice := sdk.AccAddress([]byte("alice___________")) + + _, err := ms.FundConsumerFeePool(ctx, &providertypes.MsgFundConsumerFeePool{ + Signer: alice.String(), ConsumerId: 999, + Amount: sdk.NewInt64Coin("uphoton", 1), + }) + require.ErrorIs(t, err, providertypes.ErrUnknownConsumerId) +} + +// TestFundConsumerFeePool_AllowedInActivePhases verifies fund is accepted in +// every phase except DELETED (REGISTERED, INITIALIZED, LAUNCHED, STOPPED). +func TestFundConsumerFeePool_AllowedInActivePhases(t *testing.T) { + allowedPhases := []providertypes.ConsumerPhase{ + providertypes.CONSUMER_PHASE_REGISTERED, + providertypes.CONSUMER_PHASE_INITIALIZED, + providertypes.CONSUMER_PHASE_LAUNCHED, + providertypes.CONSUMER_PHASE_STOPPED, + } + for _, phase := range allowedPhases { + t.Run(phase.String(), func(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, phase) + k.SetParams(ctx, providertypes.DefaultParams()) + params := k.GetParams(ctx) + params.FeesPerBlock = sdk.NewInt64Coin("uphoton", 10) + k.SetParams(ctx, params) + + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + amount := sdk.NewInt64Coin("uphoton", 100) + + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 0)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, alice, providertypes.ModuleName, sdk.NewCoins(amount)).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, poolAddr, sdk.NewCoins(amount)).Return(nil) + + _, err := ms.FundConsumerFeePool(ctx, &providertypes.MsgFundConsumerFeePool{ + Signer: alice.String(), ConsumerId: consumerId, Amount: amount, + }) + require.NoError(t, err) + }) + } +} + +func TestFundConsumerFeePool_RejectsDeleted(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_DELETED) + alice := sdk.AccAddress([]byte("alice___________")) + + _, err := ms.FundConsumerFeePool(ctx, &providertypes.MsgFundConsumerFeePool{ + Signer: alice.String(), ConsumerId: consumerId, + Amount: sdk.NewInt64Coin("uphoton", 1), + }) + require.ErrorIs(t, err, providertypes.ErrInvalidPhase) +} + +func TestWithdrawConsumerFeePool_Regular(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + // alice sole depositor: 100 shares, balance 80 + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(100))) + + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 80)) + // alice asks for 30, partial path: shares_to_burn = 30*100/80 = 37, tokens = 37*80/100 = 29. + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providertypes.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 29))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, alice, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 29))).Return(nil) + + resp, err := ms.WithdrawConsumerFeePool(ctx, &providertypes.MsgWithdrawConsumerFeePool{ + Signer: alice.String(), ConsumerId: consumerId, + Amount: sdk.NewCoins(sdk.NewInt64Coin("uphoton", 30)), + }) + require.NoError(t, err) + require.Equal(t, "29uphoton", resp.Amount.String()) +} + +func TestWithdrawConsumerFeePool_GovClawback(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + distrAddr := authtypes.NewModuleAddress(disttypes.ModuleName) + providerAddr := authtypes.NewModuleAddress(providertypes.ModuleName) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", distrAddr), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(100))) + + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 100)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providertypes.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100))).Return(nil) + // Gov clawback: tokens forwarded to community pool via FundCommunityPool. + mocks.MockDistributionKeeper.EXPECT().FundCommunityPool( + ctx, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100)), providerAddr).Return(nil) + + _, err := ms.WithdrawConsumerFeePool(ctx, &providertypes.MsgWithdrawConsumerFeePool{ + Signer: k.GetAuthority(), ConsumerId: consumerId, + Amount: sdk.NewCoins(sdk.NewInt64Coin("uphoton", 1_000_000)), + }) + require.NoError(t, err) +} + +func TestSweepConsumerFeePool_OwnerOnly(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + // Use a valid bech32 owner address + owner := sdk.AccAddress([]byte("owner___________")) + k.SetConsumerOwnerAddress(ctx, consumerId, owner.String()) + + notOwner := sdk.AccAddress([]byte("not-owner_______")) + _, err := ms.SweepConsumerFeePool(ctx, &providertypes.MsgSweepConsumerFeePool{ + Signer: notOwner.String(), ConsumerId: consumerId, Denoms: nil, + }) + require.ErrorIs(t, err, providertypes.ErrUnauthorized) +} + +func TestSweepConsumerFeePool_OwnerTriggers(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + owner := sdk.AccAddress([]byte("owner___________")) + k.SetConsumerOwnerAddress(ctx, consumerId, owner.String()) + + alice := sdk.AccAddress([]byte("alice___________")) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + require.NoError(t, k.ConsumerFeePoolShares.Set(ctx, + collections.Join3(consumerId, "uphoton", alice), math.NewInt(100))) + require.NoError(t, k.ConsumerFeePoolTotalShares.Set(ctx, + collections.Join(consumerId, "uphoton"), math.NewInt(100))) + + mocks.MockBankKeeper.EXPECT().GetAllBalances(ctx, poolAddr). + Return(sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100))) + mocks.MockBankKeeper.EXPECT().GetBalance(ctx, poolAddr, "uphoton"). + Return(sdk.NewInt64Coin("uphoton", 100)) + mocks.MockBankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, poolAddr, providertypes.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100))).Return(nil) + mocks.MockBankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, providertypes.ModuleName, alice, sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100))).Return(nil) + + _, err := ms.SweepConsumerFeePool(ctx, &providertypes.MsgSweepConsumerFeePool{ + Signer: owner.String(), ConsumerId: consumerId, Denoms: nil, + }) + require.NoError(t, err) +} + +func TestFundConsumerFeePool_RejectsWrongDenom(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + ms := providerkeeper.NewMsgServerImpl(&k) + + consumerId := k.FetchAndIncrementConsumerId(ctx) + k.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_REGISTERED) + k.SetParams(ctx, providertypes.DefaultParams()) + params := k.GetParams(ctx) + params.FeesPerBlock = sdk.NewInt64Coin("uphoton", 10) + k.SetParams(ctx, params) + alice := sdk.AccAddress([]byte("alice___________")) + + _, err := ms.FundConsumerFeePool(ctx, &providertypes.MsgFundConsumerFeePool{ + Signer: alice.String(), ConsumerId: consumerId, + Amount: sdk.NewInt64Coin("uatone", 1), + }) + require.ErrorIs(t, err, providertypes.ErrInvalidFundDenom) +} diff --git a/x/vaas/provider/keeper/send_restriction.go b/x/vaas/provider/keeper/send_restriction.go new file mode 100644 index 0000000..86eb098 --- /dev/null +++ b/x/vaas/provider/keeper/send_restriction.go @@ -0,0 +1,40 @@ +package keeper + +import ( + "context" + + "github.com/allinbits/vaas/x/vaas/provider/types" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// FeePoolSendRestriction returns a bank send-restriction that rejects sends +// to a known active consumer fee pool address unless the sender is the +// provider module account or the distribution module account. +func (k Keeper) FeePoolSendRestriction() func( + ctx context.Context, fromAddr, toAddr sdk.AccAddress, amount sdk.Coins, +) (sdk.AccAddress, error) { + providerAddr := authtypes.NewModuleAddress(types.ModuleName) + distrAddr := authtypes.NewModuleAddress(disttypes.ModuleName) + return func( + ctx context.Context, fromAddr, toAddr sdk.AccAddress, amount sdk.Coins, + ) (sdk.AccAddress, error) { + isFeePool, err := k.FeePoolAddressToConsumerId.Has(ctx, toAddr) + if err != nil { + return nil, errorsmod.Wrapf(err, + "fee-pool send restriction lookup for %s", toAddr.String()) + } + if !isFeePool { + return toAddr, nil + } + if fromAddr.Equals(providerAddr) || fromAddr.Equals(distrAddr) { + return toAddr, nil + } + return nil, errorsmod.Wrapf(types.ErrUnsolicitedFeePoolDeposit, + "direct send to consumer fee pool %s blocked", toAddr.String()) + } +} diff --git a/x/vaas/provider/keeper/send_restriction_test.go b/x/vaas/provider/keeper/send_restriction_test.go new file mode 100644 index 0000000..c20a565 --- /dev/null +++ b/x/vaas/provider/keeper/send_restriction_test.go @@ -0,0 +1,146 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + + testkeeper "github.com/allinbits/vaas/testutil/keeper" + providertypes "github.com/allinbits/vaas/x/vaas/provider/types" +) + +func TestFeePoolSendRestriction(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + providerAddr := authtypes.NewModuleAddress(providertypes.ModuleName) + user := sdk.AccAddress([]byte("user____________")) + other := sdk.AccAddress([]byte("other___________")) + + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolAddr, consumerId)) + + restrict := k.FeePoolSendRestriction() + amt := sdk.NewCoins(sdk.NewInt64Coin("uphoton", 1)) + + // unrelated destination passes through + to, err := restrict(ctx, user, other, amt) + require.NoError(t, err) + require.Equal(t, other, to) + + // direct send to fee pool blocked + _, err = restrict(ctx, user, poolAddr, amt) + require.ErrorIs(t, err, providertypes.ErrUnsolicitedFeePoolDeposit) + + // sanctioned 2-hop send from provider module allowed + to, err = restrict(ctx, providerAddr, poolAddr, amt) + require.NoError(t, err) + require.Equal(t, poolAddr, to) +} + +// TestFeePoolSendRestriction_BlocksUnsanctionedSenders ensures that the +// restriction blocks sends from module accounts and EOAs other than the two +// sanctioned ones (provider, distribution). +func TestFeePoolSendRestriction_BlocksUnsanctionedSenders(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolAddr, consumerId)) + restrict := k.FeePoolSendRestriction() + amt := sdk.NewCoins(sdk.NewInt64Coin("uphoton", 1)) + + senders := []sdk.AccAddress{ + authtypes.NewModuleAddress("gov"), + authtypes.NewModuleAddress("transfer"), + sdk.AccAddress([]byte("attacker________")), + } + for _, from := range senders { + _, err := restrict(ctx, from, poolAddr, amt) + require.ErrorIs(t, err, providertypes.ErrUnsolicitedFeePoolDeposit, + "sender %s should be blocked", from.String()) + } +} + +// TestFeePoolSendRestriction_CrossConsumerIsolation: a send to one consumer's +// fee pool from another consumer's fee pool is blocked — fee pool addresses +// are not on the sanctioned-sender list. +func TestFeePoolSendRestriction_CrossConsumerIsolation(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + poolA := k.GetConsumerFeePoolAddress(0) + poolB := k.GetConsumerFeePoolAddress(1) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolA, uint64(0))) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolB, uint64(1))) + + restrict := k.FeePoolSendRestriction() + amt := sdk.NewCoins(sdk.NewInt64Coin("uphoton", 1)) + _, err := restrict(ctx, poolA, poolB, amt) + require.ErrorIs(t, err, providertypes.ErrUnsolicitedFeePoolDeposit) +} + +// TestFeePoolSendRestriction_DeletedConsumerPassesThrough: once a consumer is +// deleted its reverse-lookup entry is gone, so sends to its (now-orphan) +// pool address pass through. This is the documented behavior — funds sent +// after delete are an unrecoverable user error, but the bank layer doesn't +// block them. +func TestFeePoolSendRestriction_DeletedConsumerPassesThrough(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + deletedPool := k.GetConsumerFeePoolAddress(42) + // Reverse-lookup entry intentionally NOT set. + restrict := k.FeePoolSendRestriction() + user := sdk.AccAddress([]byte("user____________")) + amt := sdk.NewCoins(sdk.NewInt64Coin("uphoton", 1)) + + to, err := restrict(ctx, user, deletedPool, amt) + require.NoError(t, err) + require.Equal(t, deletedPool, to) +} + +// TestFeePoolSendRestriction_OutboundFromPoolUnaffected: sends FROM a fee +// pool (fee collection, withdraw, sweep) pass regardless of the destination. +func TestFeePoolSendRestriction_OutboundFromPoolUnaffected(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolAddr, consumerId)) + + restrict := k.FeePoolSendRestriction() + recipient := sdk.AccAddress([]byte("recipient_______")) + amt := sdk.NewCoins(sdk.NewInt64Coin("uphoton", 1)) + + to, err := restrict(ctx, poolAddr, recipient, amt) + require.NoError(t, err) + require.Equal(t, recipient, to) +} + +// TestFeePoolSendRestriction_DistributionSenderAllowed: sends from the +// distribution module account to a fee pool are allowed (gov-fund path uses +// DistributeFromFeePool to deposit directly). +func TestFeePoolSendRestriction_DistributionSenderAllowed(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := uint64(0) + poolAddr := k.GetConsumerFeePoolAddress(consumerId) + require.NoError(t, k.FeePoolAddressToConsumerId.Set(ctx, poolAddr, consumerId)) + + restrict := k.FeePoolSendRestriction() + distrAddr := authtypes.NewModuleAddress(disttypes.ModuleName) + amt := sdk.NewCoins(sdk.NewInt64Coin("uphoton", 1)) + + to, err := restrict(ctx, distrAddr, poolAddr, amt) + require.NoError(t, err) + require.Equal(t, poolAddr, to) +} diff --git a/x/vaas/provider/types/codec.go b/x/vaas/provider/types/codec.go index 7021812..acb3b0a 100644 --- a/x/vaas/provider/types/codec.go +++ b/x/vaas/provider/types/codec.go @@ -24,6 +24,9 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { &MsgUpdateParams{}, &MsgSubmitConsumerMisbehaviour{}, &MsgSubmitConsumerDoubleVoting{}, + &MsgFundConsumerFeePool{}, + &MsgWithdrawConsumerFeePool{}, + &MsgSweepConsumerFeePool{}, ) registry.RegisterImplementations( (*exported.ClientMessage)(nil), diff --git a/x/vaas/provider/types/errors.go b/x/vaas/provider/types/errors.go index f97d3fd..3f7010f 100644 --- a/x/vaas/provider/types/errors.go +++ b/x/vaas/provider/types/errors.go @@ -28,4 +28,11 @@ var ( ErrInvalidMsgSubmitConsumerDoubleVoting = errorsmod.Register(ModuleName, 20, "invalid submit consumer double voting message") ErrInvalidConsumerInfractionParameters = errorsmod.Register(ModuleName, 21, "invalid consumer infraction parameters") ErrDuplicateChainId = errorsmod.Register(ModuleName, 22, "consumer chain-id is already in use") + ErrPoolEmpty = errorsmod.Register(ModuleName, 23, "consumer fee pool has zero balance for the requested denom") + ErrUnsolicitedFeePoolDeposit = errorsmod.Register(ModuleName, 24, "direct sends to consumer fee pool addresses are not permitted; use MsgFundConsumerFeePool") + ErrInvalidFundDenom = errorsmod.Register(ModuleName, 25, "deposit denom does not match the current fees_per_block denom") + ErrFeePoolSweepFailed = errorsmod.Register(ModuleName, 26, "consumer fee pool sweep failed") + ErrDepositTooSmall = errorsmod.Register(ModuleName, 27, "deposit too small to mint any shares") + ErrSubShareWithdraw = errorsmod.Register(ModuleName, 28, "withdraw amount too small to burn any shares") + ErrNoSharesForDepositor = errorsmod.Register(ModuleName, 29, "depositor has no shares in the consumer fee pool for the requested denom") ) diff --git a/x/vaas/provider/types/events.go b/x/vaas/provider/types/events.go index 3d66360..b89e89a 100644 --- a/x/vaas/provider/types/events.go +++ b/x/vaas/provider/types/events.go @@ -8,6 +8,9 @@ const ( EventTypeCreateConsumer = "create_consumer" EventTypeUpdateConsumer = "update_consumer" EventTypeRemoveConsumer = "remove_consumer" + EventTypeConsumerFeePoolFund = "consumer_fee_pool_fund" + EventTypeConsumerFeePoolWithdraw = "consumer_fee_pool_withdraw" + EventTypeConsumerFeePoolSweep = "consumer_fee_pool_sweep" AttributeInfractionHeight = "infraction_height" AttributeInitialHeight = "initial_height" @@ -23,4 +26,15 @@ const ( AttributeConsumerOwner = "consumer_owner" AttributeConsumerSpawnTime = "consumer_spawn_time" AttributeConsumerPhase = "consumer_phase" + AttributeDepositor = "depositor" + AttributeRecipient = "recipient" + AttributeAmount = "amount" + AttributeDenom = "denom" + AttributeTotalDistributed = "total_distributed" + AttributeDust = "dust" + AttributeWithdrawPath = "withdraw_path" + + // AttributeWithdrawPath values + WithdrawPathDirect = "direct" + WithdrawPathCommunityPool = "community_pool" ) diff --git a/x/vaas/provider/types/genesis.go b/x/vaas/provider/types/genesis.go index aadad2b..0bc0e40 100644 --- a/x/vaas/provider/types/genesis.go +++ b/x/vaas/provider/types/genesis.go @@ -19,6 +19,7 @@ func NewGenesisState( validatorConsumerPubkeys []ValidatorConsumerPubKey, validatorsByConsumerAddr []ValidatorByConsumerAddr, consumerAddrsToPrune []ConsumerAddrsToPrune, + consumerFeePoolShares []ConsumerFeePoolShare, ) *GenesisState { return &GenesisState{ ValsetUpdateId: vscID, @@ -28,6 +29,7 @@ func NewGenesisState( ValidatorConsumerPubkeys: validatorConsumerPubkeys, ValidatorsByConsumerAddr: validatorsByConsumerAddr, ConsumerAddrsToPrune: consumerAddrsToPrune, + ConsumerFeePoolShares: consumerFeePoolShares, } } @@ -75,6 +77,53 @@ func (gs GenesisState) Validate() error { return err } + if err := validateConsumerFeePoolShares(gs.ConsumerFeePoolShares, seenConsumerIds); err != nil { + return errorsmod.Wrap(vaastypes.ErrInvalidGenesis, err.Error()) + } + + return nil +} + +// validateConsumerFeePoolShares rejects malformed share records before +// InitGenesis would otherwise panic on them: bad bech32, empty denom, +// non-positive shares, duplicate (consumer, depositor, denom) triples, +// and orphan consumer ids not present in ConsumerStates. +func validateConsumerFeePoolShares( + shares []ConsumerFeePoolShare, knownConsumerIds map[uint64]bool, +) error { + type triple struct { + consumerId uint64 + depositor string + denom string + } + seen := map[triple]bool{} + for _, s := range shares { + if _, err := sdk.AccAddressFromBech32(s.Depositor); err != nil { + return fmt.Errorf("invalid depositor %q for consumer %d: %w", + s.Depositor, s.ConsumerId, err) + } + if err := sdk.ValidateDenom(s.Denom); err != nil { + return fmt.Errorf("invalid denom %q for consumer %d depositor %s: %w", + s.Denom, s.ConsumerId, s.Depositor, err) + } + if s.Shares.IsNil() { + return fmt.Errorf("nil shares for consumer %d depositor %s denom %s", + s.ConsumerId, s.Depositor, s.Denom) + } + if !s.Shares.IsPositive() { + return fmt.Errorf("non-positive shares for consumer %d depositor %s denom %s: %s", + s.ConsumerId, s.Depositor, s.Denom, s.Shares) + } + if !knownConsumerIds[s.ConsumerId] { + return fmt.Errorf("share record references unknown consumer %d", s.ConsumerId) + } + k := triple{s.ConsumerId, s.Depositor, s.Denom} + if seen[k] { + return fmt.Errorf("duplicate share record (consumer=%d, depositor=%s, denom=%s)", + s.ConsumerId, s.Depositor, s.Denom) + } + seen[k] = true + } return nil } diff --git a/x/vaas/provider/types/genesis.pb.go b/x/vaas/provider/types/genesis.pb.go index c9341c3..85f626e 100644 --- a/x/vaas/provider/types/genesis.pb.go +++ b/x/vaas/provider/types/genesis.pb.go @@ -4,8 +4,10 @@ package types import ( + cosmossdk_io_math "cosmossdk.io/math" fmt "fmt" types "github.com/allinbits/vaas/x/vaas/types" + _ "github.com/cosmos/cosmos-proto" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" @@ -43,6 +45,8 @@ type GenesisState struct { ValidatorsByConsumerAddr []ValidatorByConsumerAddr `protobuf:"bytes,6,rep,name=validators_by_consumer_addr,json=validatorsByConsumerAddr,proto3" json:"validators_by_consumer_addr"` // empty for a new chain ConsumerAddrsToPrune []ConsumerAddrsToPrune `protobuf:"bytes,7,rep,name=consumer_addrs_to_prune,json=consumerAddrsToPrune,proto3" json:"consumer_addrs_to_prune"` + // empty for a new chain + ConsumerFeePoolShares []ConsumerFeePoolShare `protobuf:"bytes,8,rep,name=consumer_fee_pool_shares,json=consumerFeePoolShares,proto3" json:"consumer_fee_pool_shares"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -127,6 +131,13 @@ func (m *GenesisState) GetConsumerAddrsToPrune() []ConsumerAddrsToPrune { return nil } +func (m *GenesisState) GetConsumerFeePoolShares() []ConsumerFeePoolShare { + if m != nil { + return m.ConsumerFeePoolShares + } + return nil +} + // The provider VAAS module's knowledge of consumer state. // // Note this type is only used internally to the provider VAAS module. @@ -339,66 +350,140 @@ func (m *ValsetUpdateIdToHeight) GetHeight() uint64 { return 0 } +// ConsumerFeePoolShare is a single depositor's share holding in a consumer +// fee pool, scoped to one denom. The triple (consumer_id, depositor, denom) +// is unique. +type ConsumerFeePoolShare struct { + ConsumerId uint64 `protobuf:"varint,1,opt,name=consumer_id,json=consumerId,proto3" json:"consumer_id,omitempty"` + Depositor string `protobuf:"bytes,2,opt,name=depositor,proto3" json:"depositor,omitempty"` + Denom string `protobuf:"bytes,3,opt,name=denom,proto3" json:"denom,omitempty"` + Shares cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=shares,proto3,customtype=cosmossdk.io/math.Int" json:"shares"` +} + +func (m *ConsumerFeePoolShare) Reset() { *m = ConsumerFeePoolShare{} } +func (m *ConsumerFeePoolShare) String() string { return proto.CompactTextString(m) } +func (*ConsumerFeePoolShare) ProtoMessage() {} +func (*ConsumerFeePoolShare) Descriptor() ([]byte, []int) { + return fileDescriptor_c9071b84cde652f9, []int{3} +} +func (m *ConsumerFeePoolShare) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsumerFeePoolShare) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsumerFeePoolShare.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 *ConsumerFeePoolShare) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsumerFeePoolShare.Merge(m, src) +} +func (m *ConsumerFeePoolShare) XXX_Size() int { + return m.Size() +} +func (m *ConsumerFeePoolShare) XXX_DiscardUnknown() { + xxx_messageInfo_ConsumerFeePoolShare.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsumerFeePoolShare proto.InternalMessageInfo + +func (m *ConsumerFeePoolShare) GetConsumerId() uint64 { + if m != nil { + return m.ConsumerId + } + return 0 +} + +func (m *ConsumerFeePoolShare) GetDepositor() string { + if m != nil { + return m.Depositor + } + return "" +} + +func (m *ConsumerFeePoolShare) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + func init() { proto.RegisterType((*GenesisState)(nil), "vaas.provider.v1.GenesisState") proto.RegisterType((*ConsumerState)(nil), "vaas.provider.v1.ConsumerState") proto.RegisterType((*ValsetUpdateIdToHeight)(nil), "vaas.provider.v1.ValsetUpdateIdToHeight") + proto.RegisterType((*ConsumerFeePoolShare)(nil), "vaas.provider.v1.ConsumerFeePoolShare") } func init() { proto.RegisterFile("vaas/provider/v1/genesis.proto", fileDescriptor_c9071b84cde652f9) } var fileDescriptor_c9071b84cde652f9 = []byte{ - // 795 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x6e, 0xeb, 0x44, - 0x18, 0x8d, 0xdb, 0x24, 0x37, 0x99, 0xa4, 0x21, 0x1a, 0x5d, 0x05, 0x93, 0xab, 0xeb, 0x44, 0x41, - 0x17, 0x05, 0x09, 0xd9, 0x6a, 0x11, 0x2c, 0x58, 0x20, 0x35, 0xad, 0x04, 0x11, 0x02, 0x45, 0x6e, - 0x61, 0xd1, 0x8d, 0x35, 0xf1, 0x0c, 0xf6, 0x10, 0xdb, 0x63, 0x79, 0x26, 0x2e, 0xe1, 0x11, 0x58, - 0xf5, 0x29, 0x78, 0x96, 0x2e, 0xbb, 0x64, 0x55, 0x50, 0xfb, 0x06, 0x3c, 0x01, 0x9a, 0xf1, 0xd8, - 0x34, 0x4d, 0x0a, 0x62, 0x67, 0x7f, 0xe7, 0xcc, 0x39, 0xdf, 0xcc, 0xf7, 0x03, 0xac, 0x1c, 0x21, - 0xee, 0xa4, 0x19, 0xcb, 0x29, 0x26, 0x99, 0x93, 0x1f, 0x3b, 0x01, 0x49, 0x08, 0xa7, 0xdc, 0x4e, - 0x33, 0x26, 0x18, 0xec, 0x4b, 0xdc, 0x2e, 0x71, 0x3b, 0x3f, 0x1e, 0xbe, 0x0e, 0x58, 0xc0, 0x14, - 0xe8, 0xc8, 0xaf, 0x82, 0x37, 0x1c, 0x05, 0x8c, 0x05, 0x11, 0x71, 0xd4, 0xdf, 0x72, 0xfd, 0xa3, - 0x23, 0x68, 0x4c, 0xb8, 0x40, 0x71, 0xaa, 0x09, 0x6f, 0x95, 0x51, 0x7e, 0xec, 0xf0, 0x10, 0x65, - 0x04, 0x7b, 0x3e, 0x4b, 0xf8, 0x3a, 0x26, 0x99, 0x86, 0x61, 0x09, 0x5f, 0xd3, 0x8c, 0x94, 0x9a, - 0x3b, 0xb9, 0x55, 0x79, 0x28, 0xc2, 0xe4, 0xd7, 0x06, 0xe8, 0x7e, 0x55, 0xa4, 0x7b, 0x21, 0x90, - 0x20, 0x70, 0x0a, 0xfa, 0x39, 0x8a, 0x38, 0x11, 0xde, 0x3a, 0xc5, 0x48, 0x10, 0x8f, 0x62, 0xd3, - 0x18, 0x1b, 0xd3, 0xba, 0xdb, 0x2b, 0xe2, 0xdf, 0xab, 0xf0, 0x1c, 0xc3, 0x10, 0xbc, 0x57, 0x66, - 0xe0, 0x71, 0x79, 0x96, 0x9b, 0x07, 0xe3, 0xc3, 0x69, 0xe7, 0x64, 0x64, 0x3f, 0xbf, 0xb1, 0x7d, - 0xa6, 0x89, 0xca, 0x63, 0x66, 0xdd, 0xde, 0x8f, 0x6a, 0x7f, 0xdd, 0x8f, 0x06, 0x1b, 0x14, 0x47, - 0x5f, 0x4c, 0x9e, 0xa9, 0x4c, 0xdc, 0x9e, 0xff, 0x94, 0xce, 0xe1, 0x4f, 0x60, 0xf8, 0x3c, 0x27, - 0x4f, 0x30, 0x2f, 0x24, 0x34, 0x08, 0x85, 0x79, 0xa8, 0x4c, 0xa7, 0xbb, 0xa6, 0x3f, 0x6c, 0xe5, - 0x7b, 0xc9, 0xbe, 0x56, 0xfc, 0x59, 0x5d, 0xba, 0xbb, 0x83, 0x7c, 0x2f, 0x0a, 0x3f, 0x07, 0xcd, - 0x14, 0x65, 0x28, 0xe6, 0x66, 0x7d, 0x6c, 0x4c, 0x3b, 0x27, 0xe6, 0xae, 0xee, 0x42, 0xe1, 0x5a, - 0x47, 0xb3, 0x61, 0xac, 0x72, 0xa4, 0x18, 0x09, 0x96, 0x55, 0x95, 0xf1, 0xd2, 0xf5, 0x72, 0x45, - 0x36, 0xdc, 0x6c, 0xa8, 0x1c, 0x3f, 0xde, 0x9b, 0x63, 0x71, 0xa6, 0x7c, 0xa1, 0xc5, 0x7a, 0xf9, - 0x0d, 0xd9, 0x68, 0x71, 0x33, 0xdf, 0x03, 0x4b, 0x41, 0x98, 0x80, 0x37, 0x15, 0xc6, 0xbd, 0xe5, - 0xe6, 0x1f, 0x4b, 0x84, 0x71, 0x66, 0x36, 0xff, 0xd3, 0x6f, 0xb6, 0x29, 0x25, 0x4f, 0x31, 0xce, - 0x76, 0xfc, 0xf8, 0x36, 0x0e, 0x7d, 0xf0, 0xfe, 0x96, 0x03, 0x97, 0x05, 0x48, 0xb3, 0x75, 0x42, - 0xcc, 0x57, 0xca, 0xeb, 0xa3, 0x97, 0x8b, 0x2e, 0x05, 0xf8, 0x25, 0x5b, 0x48, 0xb6, 0x36, 0x7a, - 0xed, 0xef, 0xc1, 0x26, 0xbf, 0x35, 0xc0, 0xd1, 0x56, 0xa7, 0xc0, 0x11, 0xe8, 0x54, 0xb6, 0x55, - 0x23, 0x82, 0x32, 0x34, 0xc7, 0xf0, 0x03, 0xd0, 0xf2, 0x43, 0x44, 0x13, 0x89, 0x1e, 0x8c, 0x8d, - 0x69, 0xdb, 0x7d, 0xa5, 0xfe, 0xe7, 0x18, 0xbe, 0x01, 0x6d, 0x3f, 0xa2, 0x24, 0x11, 0x12, 0x3b, - 0x54, 0x58, 0xab, 0x08, 0xcc, 0x31, 0x7c, 0x07, 0x7a, 0x34, 0xa1, 0x82, 0xa2, 0xa8, 0x6c, 0xa3, - 0xba, 0xd2, 0x3e, 0xd2, 0x51, 0xdd, 0x0d, 0xdf, 0x81, 0x7e, 0xe5, 0xaf, 0xa7, 0xda, 0x6c, 0xa8, - 0xbe, 0x78, 0x5b, 0xdc, 0xf7, 0xc9, 0x35, 0x9f, 0x8e, 0x91, 0xbe, 0x66, 0x35, 0x20, 0x1a, 0x83, - 0x08, 0x0c, 0x52, 0x92, 0x60, 0x9a, 0x04, 0x9e, 0xee, 0x68, 0x3f, 0x44, 0x49, 0x40, 0xb8, 0xae, - 0xd8, 0xbb, 0x4a, 0xb5, 0x2a, 0xd4, 0x05, 0x11, 0x67, 0x8a, 0xb3, 0x40, 0xfe, 0x8a, 0x88, 0x73, - 0x24, 0x50, 0xf9, 0x88, 0x5a, 0xaa, 0xe8, 0xf3, 0x82, 0xc4, 0xe1, 0x27, 0x00, 0xf2, 0x08, 0xf1, - 0xd0, 0xc3, 0xec, 0x3a, 0x91, 0x2b, 0xc4, 0x43, 0xfe, 0x4a, 0x15, 0xa9, 0xed, 0xf6, 0x15, 0x72, - 0xae, 0x81, 0x53, 0x7f, 0x05, 0x3f, 0x03, 0x8d, 0x34, 0x44, 0x9c, 0x98, 0xad, 0xb1, 0x31, 0xed, - 0xfd, 0xdb, 0xe8, 0x2e, 0x24, 0xcd, 0x2d, 0xd8, 0xf0, 0x43, 0x70, 0xc4, 0xae, 0x13, 0xdd, 0x0b, - 0x84, 0x73, 0xb3, 0xad, 0xde, 0xb7, 0xab, 0x82, 0xa7, 0x45, 0x0c, 0x7e, 0x09, 0x5a, 0x31, 0x11, - 0x08, 0x23, 0x81, 0x4c, 0xa0, 0x1e, 0x6d, 0xf2, 0xb2, 0xfc, 0xb7, 0x9a, 0xe9, 0x56, 0x67, 0xe0, - 0x05, 0xe8, 0xc8, 0x6a, 0x78, 0x7a, 0x1e, 0x3b, 0x4a, 0xe2, 0xe4, 0x65, 0x89, 0x79, 0x51, 0x3a, - 0xfa, 0x0b, 0x12, 0x94, 0x25, 0x6a, 0x4a, 0x89, 0x20, 0x19, 0x77, 0x81, 0x94, 0x29, 0xa6, 0x16, - 0x9e, 0x81, 0x6e, 0x46, 0x62, 0x96, 0xa3, 0xc8, 0x93, 0x6f, 0x60, 0x76, 0x95, 0xea, 0xd0, 0x2e, - 0x96, 0xaf, 0x5d, 0x2e, 0x5f, 0xfb, 0xb2, 0x5c, 0xbe, 0xb3, 0xfa, 0xcd, 0x1f, 0x23, 0xc3, 0xed, - 0xe8, 0x53, 0x32, 0x3e, 0xb9, 0x02, 0x83, 0xfd, 0xcb, 0xe5, 0x7f, 0xac, 0xcf, 0x01, 0x68, 0xea, - 0xce, 0x3b, 0x50, 0xb8, 0xfe, 0x9b, 0xcd, 0x6f, 0x1f, 0x2c, 0xe3, 0xee, 0xc1, 0x32, 0xfe, 0x7c, - 0xb0, 0x8c, 0x9b, 0x47, 0xab, 0x76, 0xf7, 0x68, 0xd5, 0x7e, 0x7f, 0xb4, 0x6a, 0x57, 0x4e, 0x40, - 0x45, 0xb8, 0x5e, 0xda, 0x3e, 0x8b, 0x1d, 0x14, 0x45, 0x34, 0x59, 0x52, 0xc1, 0x1d, 0xb5, 0xe1, - 0x7f, 0x76, 0xb6, 0x17, 0xbd, 0xd8, 0xa4, 0x84, 0x2f, 0x9b, 0xea, 0x36, 0x9f, 0xfe, 0x1d, 0x00, - 0x00, 0xff, 0xff, 0x5e, 0xf7, 0x5c, 0x97, 0xa2, 0x06, 0x00, 0x00, + // 933 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xdd, 0x6e, 0x1b, 0x45, + 0x18, 0xcd, 0x26, 0x76, 0x6a, 0x8f, 0x93, 0x10, 0x8d, 0x5c, 0xb3, 0x4d, 0x55, 0xdb, 0x32, 0x2a, + 0x32, 0x82, 0xee, 0x2a, 0x41, 0xf4, 0x82, 0x0b, 0xa4, 0x38, 0x15, 0x60, 0x21, 0x90, 0xb5, 0x0e, + 0x5c, 0xf4, 0x66, 0x35, 0xde, 0x99, 0xee, 0x0e, 0xde, 0xdd, 0x59, 0xed, 0x8c, 0x37, 0x98, 0xa7, + 0xe8, 0x53, 0xf0, 0x04, 0x7d, 0x88, 0x8a, 0xab, 0xaa, 0x57, 0x88, 0x8b, 0x80, 0x92, 0x27, 0x80, + 0x27, 0x40, 0xf3, 0xb3, 0xdb, 0x38, 0x71, 0x88, 0x7a, 0xe7, 0xf9, 0xce, 0x99, 0x73, 0xbe, 0x9d, + 0xef, 0xc7, 0xa0, 0x5b, 0x20, 0xc4, 0xdd, 0x2c, 0x67, 0x05, 0xc5, 0x24, 0x77, 0x8b, 0x43, 0x37, + 0x24, 0x29, 0xe1, 0x94, 0x3b, 0x59, 0xce, 0x04, 0x83, 0xfb, 0x12, 0x77, 0x4a, 0xdc, 0x29, 0x0e, + 0x0f, 0xda, 0x21, 0x0b, 0x99, 0x02, 0x5d, 0xf9, 0x4b, 0xf3, 0x0e, 0x7a, 0x21, 0x63, 0x61, 0x4c, + 0x5c, 0x75, 0x9a, 0x2d, 0x5e, 0xb8, 0x82, 0x26, 0x84, 0x0b, 0x94, 0x64, 0x86, 0xf0, 0x20, 0x60, + 0x3c, 0x61, 0xdc, 0xd7, 0x37, 0xf5, 0xc1, 0x40, 0x8f, 0x54, 0x0e, 0xc5, 0xa1, 0xcb, 0x23, 0x94, + 0x13, 0xec, 0x07, 0x2c, 0xe5, 0x8b, 0x84, 0xe4, 0x06, 0x86, 0x25, 0x7c, 0x46, 0x73, 0x52, 0xda, + 0xdd, 0x48, 0xbb, 0x4a, 0x51, 0x11, 0x06, 0xff, 0xd4, 0xc1, 0xce, 0x37, 0xfa, 0x4b, 0xa6, 0x02, + 0x09, 0x02, 0x87, 0x60, 0xbf, 0x40, 0x31, 0x27, 0xc2, 0x5f, 0x64, 0x18, 0x09, 0xe2, 0x53, 0x6c, + 0x5b, 0x7d, 0x6b, 0x58, 0xf3, 0xf6, 0x74, 0xfc, 0x47, 0x15, 0x1e, 0x63, 0x18, 0x81, 0x0f, 0xca, + 0x0c, 0x7c, 0x2e, 0xef, 0x72, 0x7b, 0xb3, 0xbf, 0x35, 0x6c, 0x1d, 0xf5, 0x9c, 0xeb, 0x8f, 0xe1, + 0x9c, 0x18, 0xa2, 0xf2, 0x18, 0x75, 0x5f, 0x9f, 0xf7, 0x36, 0xfe, 0x3d, 0xef, 0x75, 0x96, 0x28, + 0x89, 0xbf, 0x1c, 0x5c, 0x53, 0x19, 0x78, 0x7b, 0xc1, 0x55, 0x3a, 0x87, 0x3f, 0x83, 0x83, 0xeb, + 0x39, 0xf9, 0x82, 0xf9, 0x11, 0xa1, 0x61, 0x24, 0xec, 0x2d, 0x65, 0x3a, 0xbc, 0x69, 0xfa, 0xd3, + 0x4a, 0xbe, 0xa7, 0xec, 0x5b, 0xc5, 0x1f, 0xd5, 0xa4, 0xbb, 0xd7, 0x29, 0xd6, 0xa2, 0xf0, 0x29, + 0xd8, 0xce, 0x50, 0x8e, 0x12, 0x6e, 0xd7, 0xfa, 0xd6, 0xb0, 0x75, 0x64, 0xdf, 0xd4, 0x9d, 0x28, + 0xdc, 0xe8, 0x18, 0x36, 0x4c, 0x54, 0x8e, 0x14, 0x23, 0xc1, 0xf2, 0xaa, 0x32, 0x7e, 0xb6, 0x98, + 0xcd, 0xc9, 0x92, 0xdb, 0x75, 0x95, 0xe3, 0x27, 0x6b, 0x73, 0xd4, 0x77, 0xca, 0x17, 0x9a, 0x2c, + 0x66, 0xdf, 0x91, 0xa5, 0x11, 0xb7, 0x8b, 0x35, 0xb0, 0x14, 0x84, 0x29, 0x78, 0x58, 0x61, 0xdc, + 0x9f, 0x2d, 0xdf, 0x59, 0x22, 0x8c, 0x73, 0x7b, 0xfb, 0x4e, 0xbf, 0xd1, 0xb2, 0x94, 0x3c, 0xc6, + 0x38, 0xbf, 0xe1, 0xc7, 0x57, 0x71, 0x18, 0x80, 0x0f, 0x57, 0x1c, 0xb8, 0x2c, 0x40, 0x96, 0x2f, + 0x52, 0x62, 0xdf, 0x53, 0x5e, 0x1f, 0xdf, 0x5e, 0x74, 0x29, 0xc0, 0x4f, 0xd9, 0x44, 0xb2, 0x8d, + 0x51, 0x3b, 0x58, 0x83, 0x41, 0x02, 0xec, 0xca, 0xe4, 0x05, 0x21, 0x7e, 0xc6, 0x58, 0xec, 0xab, + 0x66, 0xe7, 0x76, 0xe3, 0x2e, 0x97, 0xaf, 0x09, 0x99, 0x30, 0x16, 0x4f, 0x25, 0xdd, 0xb8, 0xdc, + 0x0f, 0xd6, 0x60, 0x7c, 0xf0, 0x5b, 0x1d, 0xec, 0xae, 0x34, 0x24, 0xec, 0x81, 0x56, 0x65, 0x5c, + 0xf5, 0x3b, 0x28, 0x43, 0x63, 0x0c, 0x1f, 0x80, 0x46, 0x10, 0x21, 0x9a, 0x4a, 0x74, 0xb3, 0x6f, + 0x0d, 0x9b, 0xde, 0x3d, 0x75, 0x1e, 0x63, 0xf8, 0x10, 0x34, 0x83, 0x98, 0x92, 0x54, 0x48, 0x6c, + 0x4b, 0x61, 0x0d, 0x1d, 0x18, 0x63, 0xf8, 0x18, 0xec, 0xd1, 0x94, 0x0a, 0x8a, 0xe2, 0xb2, 0x5b, + 0x6b, 0x4a, 0x7b, 0xd7, 0x44, 0x4d, 0xd3, 0xfd, 0x00, 0xf6, 0x2b, 0x7f, 0xb3, 0x57, 0xec, 0xba, + 0x6a, 0xbf, 0x47, 0xfa, 0x83, 0xaf, 0x7c, 0xe7, 0xd5, 0x69, 0x35, 0xdf, 0x59, 0xcd, 0xa1, 0xc1, + 0x20, 0x02, 0x9d, 0x8c, 0xa4, 0x98, 0xa6, 0xa1, 0x6f, 0x06, 0x27, 0x88, 0x50, 0x1a, 0x12, 0x6e, + 0x1a, 0xe3, 0x71, 0xa5, 0x5a, 0xf5, 0xc3, 0x94, 0x88, 0x13, 0xc5, 0x99, 0xa0, 0x60, 0x4e, 0xc4, + 0x33, 0x24, 0x50, 0x59, 0x2b, 0x23, 0xa5, 0xc7, 0x49, 0x93, 0x38, 0xfc, 0x0c, 0x40, 0x1e, 0x23, + 0x1e, 0xf9, 0x98, 0x9d, 0xa5, 0x72, 0x89, 0xf9, 0x28, 0x98, 0xab, 0x5e, 0x68, 0x7a, 0xfb, 0x0a, + 0x79, 0x66, 0x80, 0xe3, 0x60, 0x0e, 0xbf, 0x00, 0xf5, 0x2c, 0x42, 0x9c, 0xd8, 0x8d, 0xbe, 0x35, + 0xdc, 0xfb, 0xbf, 0x0d, 0x31, 0x91, 0x34, 0x4f, 0xb3, 0xe1, 0x47, 0x60, 0x97, 0x9d, 0xa5, 0xa6, + 0xe5, 0x08, 0xe7, 0x76, 0x53, 0xbd, 0xef, 0x8e, 0x0a, 0x1e, 0xeb, 0x18, 0xfc, 0x0a, 0x34, 0x12, + 0x22, 0x10, 0x46, 0x02, 0xd9, 0x40, 0x3d, 0xda, 0xe0, 0x76, 0xf9, 0xef, 0x0d, 0xd3, 0xab, 0xee, + 0xc0, 0x29, 0x68, 0xc9, 0x6a, 0xf8, 0x66, 0xec, 0x5b, 0x4a, 0xe2, 0xe8, 0x76, 0x89, 0xb1, 0x2e, + 0x1d, 0xfd, 0x15, 0x09, 0xca, 0x52, 0xb5, 0x0c, 0x88, 0x20, 0x39, 0xf7, 0x80, 0x94, 0xd1, 0xcb, + 0x01, 0x9e, 0x80, 0x9d, 0x9c, 0x24, 0xac, 0x40, 0xb1, 0x2f, 0xdf, 0xc0, 0xde, 0x51, 0xaa, 0x07, + 0x8e, 0x5e, 0xff, 0x4e, 0xb9, 0xfe, 0x9d, 0xd3, 0x72, 0xfd, 0x8f, 0x6a, 0x2f, 0xff, 0xea, 0x59, + 0x5e, 0xcb, 0xdc, 0x92, 0xf1, 0xc1, 0x73, 0xd0, 0x59, 0xbf, 0xc3, 0xde, 0x63, 0x4b, 0x77, 0xc0, + 0xb6, 0xe9, 0xbc, 0x4d, 0x85, 0x9b, 0xd3, 0xe0, 0x77, 0x0b, 0xb4, 0xd7, 0x8d, 0xce, 0xdd, 0xb3, + 0xf0, 0x14, 0x34, 0x31, 0xc9, 0x18, 0xa7, 0x82, 0xe5, 0x7a, 0x18, 0x46, 0xf6, 0xdb, 0x57, 0x4f, + 0xda, 0xe6, 0xbf, 0xca, 0x94, 0x65, 0x2a, 0x72, 0x9a, 0x86, 0xde, 0x3b, 0x2a, 0x6c, 0x83, 0x3a, + 0x26, 0x29, 0x4b, 0xcc, 0x90, 0xe8, 0x03, 0x3c, 0x01, 0xdb, 0x66, 0xc2, 0x6b, 0x4a, 0xea, 0x53, + 0xd9, 0x73, 0x7f, 0x9e, 0xf7, 0xee, 0x6b, 0x39, 0x8e, 0xe7, 0x0e, 0x65, 0x6e, 0x82, 0x44, 0xe4, + 0x8c, 0x53, 0xf1, 0xf6, 0xd5, 0x13, 0x60, 0x7c, 0xc6, 0xa9, 0xf0, 0xcc, 0xd5, 0xd1, 0xf8, 0xf5, + 0x45, 0xd7, 0x7a, 0x73, 0xd1, 0xb5, 0xfe, 0xbe, 0xe8, 0x5a, 0x2f, 0x2f, 0xbb, 0x1b, 0x6f, 0x2e, + 0xbb, 0x1b, 0x7f, 0x5c, 0x76, 0x37, 0x9e, 0xbb, 0x21, 0x15, 0xd1, 0x62, 0xe6, 0x04, 0x2c, 0x71, + 0x51, 0x1c, 0xd3, 0x74, 0x46, 0x05, 0x77, 0xd5, 0xbf, 0xe2, 0x2f, 0xee, 0xea, 0x9f, 0xa3, 0x58, + 0x66, 0x84, 0xcf, 0xb6, 0x55, 0x69, 0x3e, 0xff, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x3c, 0xbe, 0x09, + 0x4a, 0xf1, 0x07, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -421,6 +506,20 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ConsumerFeePoolShares) > 0 { + for iNdEx := len(m.ConsumerFeePoolShares) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ConsumerFeePoolShares[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } if len(m.ConsumerAddrsToPrune) > 0 { for iNdEx := len(m.ConsumerAddrsToPrune) - 1; iNdEx >= 0; iNdEx-- { { @@ -668,6 +767,58 @@ func (m *ValsetUpdateIdToHeight) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *ConsumerFeePoolShare) 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 *ConsumerFeePoolShare) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsumerFeePoolShare) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Shares.Size() + i -= size + if _, err := m.Shares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0x1a + } + if len(m.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Depositor))) + i-- + dAtA[i] = 0x12 + } + if m.ConsumerId != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.ConsumerId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { offset -= sovGenesis(v) base := offset @@ -720,6 +871,12 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + if len(m.ConsumerFeePoolShares) > 0 { + for _, e := range m.ConsumerFeePoolShares { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } return n } @@ -794,6 +951,28 @@ func (m *ValsetUpdateIdToHeight) Size() (n int) { return n } +func (m *ConsumerFeePoolShare) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ConsumerId != 0 { + n += 1 + sovGenesis(uint64(m.ConsumerId)) + } + l = len(m.Depositor) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.Shares.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + func sovGenesis(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1051,6 +1230,40 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerFeePoolShares", 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 + } + m.ConsumerFeePoolShares = append(m.ConsumerFeePoolShares, ConsumerFeePoolShare{}) + if err := m.ConsumerFeePoolShares[len(m.ConsumerFeePoolShares)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) @@ -1570,6 +1783,173 @@ func (m *ValsetUpdateIdToHeight) Unmarshal(dAtA []byte) error { } return nil } +func (m *ConsumerFeePoolShare) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConsumerFeePoolShare: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsumerFeePoolShare: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerId", wireType) + } + m.ConsumerId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ConsumerId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depositor", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Depositor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Shares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipGenesis(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/vaas/provider/types/genesis_test.go b/x/vaas/provider/types/genesis_test.go index 58e7550..9ed7317 100644 --- a/x/vaas/provider/types/genesis_test.go +++ b/x/vaas/provider/types/genesis_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + sdkmath "cosmossdk.io/math" + "github.com/allinbits/vaas/testutil/crypto" "github.com/allinbits/vaas/x/vaas/provider/types" vaastypes "github.com/allinbits/vaas/x/vaas/types" @@ -53,6 +55,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), true, }, @@ -71,6 +74,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), true, }, @@ -85,6 +89,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), true, }, @@ -98,6 +103,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), false, }, @@ -111,6 +117,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), false, }, @@ -126,6 +133,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), false, }, @@ -142,6 +150,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), false, }, @@ -155,6 +164,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), false, }, @@ -168,6 +178,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), true, }, @@ -185,6 +196,7 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, ), false, }, @@ -203,6 +215,104 @@ func TestValidateGenesisState(t *testing.T) { } } +func TestValidateGenesisState_FeePoolShares(t *testing.T) { + alice := sdk.AccAddress([]byte("alice___________")).String() + bob := sdk.AccAddress([]byte("bob_____________")).String() + cs := types.ConsumerState{ + ConsumerId: 0, + ChainId: "chain-1", + Phase: types.CONSUMER_PHASE_REGISTERED, + OwnerAddress: sdk.AccAddress([]byte("vaas-test-owner-1234")).String(), + } + + build := func(shares ...types.ConsumerFeePoolShare) *types.GenesisState { + gs := types.NewGenesisState( + types.DefaultValsetUpdateID, nil, + []types.ConsumerState{cs}, + types.DefaultParams(), + nil, nil, nil, + shares, + ) + return gs + } + + t.Run("valid share record", func(t *testing.T) { + err := build(types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: alice, Denom: "uphoton", + Shares: sdkmath.NewInt(100), + }).Validate() + require.NoError(t, err) + }) + + t.Run("invalid depositor bech32", func(t *testing.T) { + err := build(types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: "not-a-bech32", Denom: "uphoton", + Shares: sdkmath.NewInt(100), + }).Validate() + require.Error(t, err) + }) + + t.Run("invalid denom", func(t *testing.T) { + err := build(types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: alice, Denom: "", + Shares: sdkmath.NewInt(100), + }).Validate() + require.Error(t, err) + }) + + t.Run("zero shares", func(t *testing.T) { + err := build(types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: alice, Denom: "uphoton", + Shares: sdkmath.ZeroInt(), + }).Validate() + require.Error(t, err) + }) + + t.Run("negative shares", func(t *testing.T) { + err := build(types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: alice, Denom: "uphoton", + Shares: sdkmath.NewInt(-1), + }).Validate() + require.Error(t, err) + }) + + t.Run("orphan consumer id", func(t *testing.T) { + err := build(types.ConsumerFeePoolShare{ + ConsumerId: 99, Depositor: alice, Denom: "uphoton", + Shares: sdkmath.NewInt(100), + }).Validate() + require.Error(t, err) + }) + + t.Run("duplicate triple", func(t *testing.T) { + err := build( + types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: alice, Denom: "uphoton", + Shares: sdkmath.NewInt(50), + }, + types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: alice, Denom: "uphoton", + Shares: sdkmath.NewInt(50), + }, + ).Validate() + require.Error(t, err) + }) + + t.Run("two depositors same denom is allowed", func(t *testing.T) { + err := build( + types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: alice, Denom: "uphoton", + Shares: sdkmath.NewInt(60), + }, + types.ConsumerFeePoolShare{ + ConsumerId: 0, Depositor: bob, Denom: "uphoton", + Shares: sdkmath.NewInt(40), + }, + ).Validate() + require.NoError(t, err) + }) +} + func TestConsumerStateValidatePerPhase(t *testing.T) { validMetadata := types.ConsumerMetadata{Name: "n", Description: "d", Metadata: "m"} validInit := &types.ConsumerInitializationParameters{ diff --git a/x/vaas/provider/types/keys.go b/x/vaas/provider/types/keys.go index 5fcda22..d3a7a37 100644 --- a/x/vaas/provider/types/keys.go +++ b/x/vaas/provider/types/keys.go @@ -80,6 +80,10 @@ const ( InfractionScheduledTimeToConsumerIdsKeyName = "InfractionScheduledTimeToConsumerIdsKeyName" ConsumerIdToDebtKeyName = "ConsumerIdToDebtKeyName" + + ConsumerFeePoolSharesKeyName = "ConsumerFeePoolSharesKey" + ConsumerFeePoolTotalSharesKeyName = "ConsumerFeePoolTotalSharesKey" + FeePoolAddressToConsumerIdKeyName = "FeePoolAddressToConsumerIdKey" ) // Collection key prefixes for use with cosmossdk.io/collections @@ -110,5 +114,8 @@ var ( ConsumerIdToQueuedInfractionPrefix = collections.NewPrefix(23) InfractionScheduledTimePrefix = collections.NewPrefix(24) ConsumerIdToDebtPrefix = collections.NewPrefix(25) + ConsumerFeePoolSharesKeyPrefix = collections.NewPrefix(26) + ConsumerFeePoolTotalSharesKeyPrefix = collections.NewPrefix(27) + FeePoolAddressToConsumerIdKeyPrefix = collections.NewPrefix(28) ParametersPrefix = collections.NewPrefix(0xFF) ) diff --git a/x/vaas/provider/types/msg.go b/x/vaas/provider/types/msg.go index 736b4c4..6a166a7 100644 --- a/x/vaas/provider/types/msg.go +++ b/x/vaas/provider/types/msg.go @@ -17,6 +17,7 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) const ( @@ -39,6 +40,9 @@ var ( _ sdk.Msg = (*MsgCreateConsumer)(nil) _ sdk.Msg = (*MsgUpdateConsumer)(nil) _ sdk.Msg = (*MsgRemoveConsumer)(nil) + _ sdk.Msg = (*MsgFundConsumerFeePool)(nil) + _ sdk.Msg = (*MsgWithdrawConsumerFeePool)(nil) + _ sdk.Msg = (*MsgSweepConsumerFeePool)(nil) _ sdk.HasValidateBasic = (*MsgAssignConsumerKey)(nil) _ sdk.HasValidateBasic = (*MsgSubmitConsumerMisbehaviour)(nil) @@ -46,6 +50,9 @@ var ( _ sdk.HasValidateBasic = (*MsgCreateConsumer)(nil) _ sdk.HasValidateBasic = (*MsgUpdateConsumer)(nil) _ sdk.HasValidateBasic = (*MsgRemoveConsumer)(nil) + _ sdk.HasValidateBasic = (*MsgFundConsumerFeePool)(nil) + _ sdk.HasValidateBasic = (*MsgWithdrawConsumerFeePool)(nil) + _ sdk.HasValidateBasic = (*MsgSweepConsumerFeePool)(nil) ) // NewMsgAssignConsumerKey creates a new MsgAssignConsumerKey instance. @@ -418,3 +425,52 @@ func ValidateInitialHeight(initialHeight clienttypes.Height, chainID string) err } return nil } + +// ValidateBasic implements the sdk.HasValidateBasic interface. +func (msg MsgFundConsumerFeePool) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid signer: %s", err) + } + if err := msg.Amount.Validate(); err != nil { + return errorsmod.Wrapf(ErrInvalidFundDenom, "invalid amount: %s", err) + } + if !msg.Amount.IsPositive() { + return errorsmod.Wrap(ErrInvalidFundDenom, "amount must be positive") + } + return nil +} + +// ValidateBasic implements the sdk.HasValidateBasic interface. +func (msg MsgWithdrawConsumerFeePool) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid signer: %s", err) + } + if len(msg.Amount) == 0 { + return errorsmod.Wrap(ErrInvalidFundDenom, "amount must not be empty") + } + if err := msg.Amount.Validate(); err != nil { + return errorsmod.Wrapf(ErrInvalidFundDenom, "invalid amount: %s", err) + } + if !msg.Amount.IsAllPositive() { + return errorsmod.Wrap(ErrInvalidFundDenom, "amounts must be positive") + } + return nil +} + +// ValidateBasic implements the sdk.HasValidateBasic interface. +func (msg MsgSweepConsumerFeePool) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid signer: %s", err) + } + seen := make(map[string]struct{}, len(msg.Denoms)) + for _, d := range msg.Denoms { + if err := sdk.ValidateDenom(d); err != nil { + return errorsmod.Wrapf(ErrInvalidFundDenom, "invalid denom %q: %s", d, err) + } + if _, dup := seen[d]; dup { + return errorsmod.Wrapf(ErrInvalidFundDenom, "duplicate denom %q", d) + } + seen[d] = struct{}{} + } + return nil +} diff --git a/x/vaas/provider/types/msg_test.go b/x/vaas/provider/types/msg_test.go index a9ad1ab..4bf3a1d 100644 --- a/x/vaas/provider/types/msg_test.go +++ b/x/vaas/provider/types/msg_test.go @@ -543,6 +543,136 @@ func TestValidateInitialHeight(t *testing.T) { } } +func TestMsgFundConsumerFeePool_ValidateBasic(t *testing.T) { + validSigner := sdk.AccAddress([]byte("alice___________")).String() + tests := []struct { + name string + msg types.MsgFundConsumerFeePool + wantErr bool + }{ + {"valid", types.MsgFundConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Amount: sdk.NewInt64Coin("uphoton", 100), + }, false}, + {"invalid signer", types.MsgFundConsumerFeePool{ + Signer: "not-bech32", + ConsumerId: 0, + Amount: sdk.NewInt64Coin("uphoton", 100), + }, true}, + {"zero amount", types.MsgFundConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Amount: sdk.NewInt64Coin("uphoton", 0), + }, true}, + {"invalid denom", types.MsgFundConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Amount: sdk.Coin{Denom: "", Amount: math.NewInt(100)}, + }, true}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgWithdrawConsumerFeePool_ValidateBasic(t *testing.T) { + validSigner := sdk.AccAddress([]byte("alice___________")).String() + tests := []struct { + name string + msg types.MsgWithdrawConsumerFeePool + wantErr bool + }{ + {"valid", types.MsgWithdrawConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Amount: sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100)), + }, false}, + {"invalid signer", types.MsgWithdrawConsumerFeePool{ + Signer: "not-bech32", + ConsumerId: 0, + Amount: sdk.NewCoins(sdk.NewInt64Coin("uphoton", 100)), + }, true}, + {"empty coins", types.MsgWithdrawConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Amount: sdk.Coins{}, + }, true}, + {"coins with zero amount", types.MsgWithdrawConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Amount: sdk.Coins{sdk.NewInt64Coin("uphoton", 0)}, + }, true}, + {"coins with duplicate denom", types.MsgWithdrawConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Amount: sdk.Coins{sdk.NewInt64Coin("uphoton", 50), sdk.NewInt64Coin("uphoton", 50)}, + }, true}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgSweepConsumerFeePool_ValidateBasic(t *testing.T) { + validSigner := sdk.AccAddress([]byte("alice___________")).String() + tests := []struct { + name string + msg types.MsgSweepConsumerFeePool + wantErr bool + }{ + {"valid with explicit denoms", types.MsgSweepConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Denoms: []string{"uphoton", "uatom"}, + }, false}, + {"valid with empty denoms", types.MsgSweepConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Denoms: []string{}, + }, false}, + {"invalid signer", types.MsgSweepConsumerFeePool{ + Signer: "not-bech32", + ConsumerId: 0, + Denoms: []string{"uphoton"}, + }, true}, + {"invalid denom string", types.MsgSweepConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Denoms: []string{"INVALID DENOM WITH SPACES"}, + }, true}, + {"duplicate denom", types.MsgSweepConsumerFeePool{ + Signer: validSigner, + ConsumerId: 0, + Denoms: []string{"uphoton", "uphoton"}, + }, true}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + func TestValidateChainId(t *testing.T) { testCases := []struct { name string diff --git a/x/vaas/provider/types/query.pb.go b/x/vaas/provider/types/query.pb.go index 4447664..5d18480 100644 --- a/x/vaas/provider/types/query.pb.go +++ b/x/vaas/provider/types/query.pb.go @@ -10,6 +10,8 @@ import ( types "github.com/allinbits/vaas/x/vaas/types" crypto "github.com/cometbft/cometbft/proto/tendermint/crypto" _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types2 "github.com/cosmos/cosmos-sdk/types" query "github.com/cosmos/cosmos-sdk/types/query" types1 "github.com/cosmos/cosmos-sdk/x/staking/types" _ "github.com/cosmos/gogoproto/gogoproto" @@ -1361,6 +1363,261 @@ func (m *QueryConsumerGenesisTimeResponse) GetGenesisTime() time.Time { return time.Time{} } +type QueryConsumerFeePoolClaimRequest struct { + ConsumerId uint64 `protobuf:"varint,1,opt,name=consumer_id,json=consumerId,proto3" json:"consumer_id,omitempty"` + // bech32 address; if equal to the gov module authority, aliases to the + // distribution module account address + Depositor string `protobuf:"bytes,2,opt,name=depositor,proto3" json:"depositor,omitempty"` +} + +func (m *QueryConsumerFeePoolClaimRequest) Reset() { *m = QueryConsumerFeePoolClaimRequest{} } +func (m *QueryConsumerFeePoolClaimRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerFeePoolClaimRequest) ProtoMessage() {} +func (*QueryConsumerFeePoolClaimRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_71b3f55ed407aa51, []int{25} +} +func (m *QueryConsumerFeePoolClaimRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerFeePoolClaimRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerFeePoolClaimRequest.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 *QueryConsumerFeePoolClaimRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerFeePoolClaimRequest.Merge(m, src) +} +func (m *QueryConsumerFeePoolClaimRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerFeePoolClaimRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerFeePoolClaimRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerFeePoolClaimRequest proto.InternalMessageInfo + +func (m *QueryConsumerFeePoolClaimRequest) GetConsumerId() uint64 { + if m != nil { + return m.ConsumerId + } + return 0 +} + +func (m *QueryConsumerFeePoolClaimRequest) GetDepositor() string { + if m != nil { + return m.Depositor + } + return "" +} + +type QueryConsumerFeePoolClaimResponse struct { + // claimable balance across all denoms; excludes zero-claim denoms + Claim github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=claim,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"claim"` +} + +func (m *QueryConsumerFeePoolClaimResponse) Reset() { *m = QueryConsumerFeePoolClaimResponse{} } +func (m *QueryConsumerFeePoolClaimResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerFeePoolClaimResponse) ProtoMessage() {} +func (*QueryConsumerFeePoolClaimResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_71b3f55ed407aa51, []int{26} +} +func (m *QueryConsumerFeePoolClaimResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerFeePoolClaimResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerFeePoolClaimResponse.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 *QueryConsumerFeePoolClaimResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerFeePoolClaimResponse.Merge(m, src) +} +func (m *QueryConsumerFeePoolClaimResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerFeePoolClaimResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerFeePoolClaimResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerFeePoolClaimResponse proto.InternalMessageInfo + +func (m *QueryConsumerFeePoolClaimResponse) GetClaim() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Claim + } + return nil +} + +type DepositorClaim struct { + Depositor string `protobuf:"bytes,1,opt,name=depositor,proto3" json:"depositor,omitempty"` + Claim github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=claim,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"claim"` +} + +func (m *DepositorClaim) Reset() { *m = DepositorClaim{} } +func (m *DepositorClaim) String() string { return proto.CompactTextString(m) } +func (*DepositorClaim) ProtoMessage() {} +func (*DepositorClaim) Descriptor() ([]byte, []int) { + return fileDescriptor_71b3f55ed407aa51, []int{27} +} +func (m *DepositorClaim) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DepositorClaim) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DepositorClaim.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 *DepositorClaim) XXX_Merge(src proto.Message) { + xxx_messageInfo_DepositorClaim.Merge(m, src) +} +func (m *DepositorClaim) XXX_Size() int { + return m.Size() +} +func (m *DepositorClaim) XXX_DiscardUnknown() { + xxx_messageInfo_DepositorClaim.DiscardUnknown(m) +} + +var xxx_messageInfo_DepositorClaim proto.InternalMessageInfo + +func (m *DepositorClaim) GetDepositor() string { + if m != nil { + return m.Depositor + } + return "" +} + +func (m *DepositorClaim) GetClaim() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Claim + } + return nil +} + +type QueryConsumerFeePoolClaimsRequest struct { + ConsumerId uint64 `protobuf:"varint,1,opt,name=consumer_id,json=consumerId,proto3" json:"consumer_id,omitempty"` + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryConsumerFeePoolClaimsRequest) Reset() { *m = QueryConsumerFeePoolClaimsRequest{} } +func (m *QueryConsumerFeePoolClaimsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerFeePoolClaimsRequest) ProtoMessage() {} +func (*QueryConsumerFeePoolClaimsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_71b3f55ed407aa51, []int{28} +} +func (m *QueryConsumerFeePoolClaimsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerFeePoolClaimsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerFeePoolClaimsRequest.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 *QueryConsumerFeePoolClaimsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerFeePoolClaimsRequest.Merge(m, src) +} +func (m *QueryConsumerFeePoolClaimsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerFeePoolClaimsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerFeePoolClaimsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerFeePoolClaimsRequest proto.InternalMessageInfo + +func (m *QueryConsumerFeePoolClaimsRequest) GetConsumerId() uint64 { + if m != nil { + return m.ConsumerId + } + return 0 +} + +func (m *QueryConsumerFeePoolClaimsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +type QueryConsumerFeePoolClaimsResponse struct { + Claims []DepositorClaim `protobuf:"bytes,1,rep,name=claims,proto3" json:"claims"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryConsumerFeePoolClaimsResponse) Reset() { *m = QueryConsumerFeePoolClaimsResponse{} } +func (m *QueryConsumerFeePoolClaimsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerFeePoolClaimsResponse) ProtoMessage() {} +func (*QueryConsumerFeePoolClaimsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_71b3f55ed407aa51, []int{29} +} +func (m *QueryConsumerFeePoolClaimsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerFeePoolClaimsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerFeePoolClaimsResponse.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 *QueryConsumerFeePoolClaimsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerFeePoolClaimsResponse.Merge(m, src) +} +func (m *QueryConsumerFeePoolClaimsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerFeePoolClaimsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerFeePoolClaimsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerFeePoolClaimsResponse proto.InternalMessageInfo + +func (m *QueryConsumerFeePoolClaimsResponse) GetClaims() []DepositorClaim { + if m != nil { + return m.Claims + } + return nil +} + +func (m *QueryConsumerFeePoolClaimsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + func init() { proto.RegisterType((*QueryConsumerGenesisRequest)(nil), "vaas.provider.v1.QueryConsumerGenesisRequest") proto.RegisterType((*QueryConsumerGenesisResponse)(nil), "vaas.provider.v1.QueryConsumerGenesisResponse") @@ -1387,124 +1644,144 @@ func init() { proto.RegisterType((*QueryConsumerChainResponse)(nil), "vaas.provider.v1.QueryConsumerChainResponse") proto.RegisterType((*QueryConsumerGenesisTimeRequest)(nil), "vaas.provider.v1.QueryConsumerGenesisTimeRequest") proto.RegisterType((*QueryConsumerGenesisTimeResponse)(nil), "vaas.provider.v1.QueryConsumerGenesisTimeResponse") + proto.RegisterType((*QueryConsumerFeePoolClaimRequest)(nil), "vaas.provider.v1.QueryConsumerFeePoolClaimRequest") + proto.RegisterType((*QueryConsumerFeePoolClaimResponse)(nil), "vaas.provider.v1.QueryConsumerFeePoolClaimResponse") + proto.RegisterType((*DepositorClaim)(nil), "vaas.provider.v1.DepositorClaim") + proto.RegisterType((*QueryConsumerFeePoolClaimsRequest)(nil), "vaas.provider.v1.QueryConsumerFeePoolClaimsRequest") + proto.RegisterType((*QueryConsumerFeePoolClaimsResponse)(nil), "vaas.provider.v1.QueryConsumerFeePoolClaimsResponse") } func init() { proto.RegisterFile("vaas/provider/v1/query.proto", fileDescriptor_71b3f55ed407aa51) } var fileDescriptor_71b3f55ed407aa51 = []byte{ - // 1794 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x4d, 0x6c, 0x1b, 0x41, - 0x15, 0xce, 0x3a, 0x3f, 0x75, 0xc6, 0x6d, 0x1a, 0x86, 0x84, 0x38, 0x6e, 0x12, 0xb7, 0xdb, 0x1f, - 0xd2, 0x9f, 0xec, 0xd6, 0x4e, 0x53, 0xa1, 0x50, 0x40, 0x71, 0x4a, 0x82, 0x55, 0x4a, 0xcd, 0x26, - 0xf4, 0x50, 0x04, 0xab, 0xb1, 0x77, 0xe2, 0x0c, 0x59, 0xef, 0x6e, 0x77, 0xd7, 0x6e, 0x4d, 0x94, - 0x0b, 0x27, 0x0e, 0x20, 0x55, 0xe2, 0xc0, 0x81, 0x4b, 0x85, 0x38, 0x72, 0xec, 0x01, 0x21, 0x71, - 0xe2, 0xd2, 0x63, 0xd5, 0x5e, 0x2a, 0x0e, 0x01, 0xb5, 0x1c, 0x38, 0x57, 0x1c, 0x38, 0x21, 0xb4, - 0xf3, 0xb3, 0xf1, 0x6e, 0x76, 0x63, 0xa7, 0xe5, 0xe6, 0x9d, 0x79, 0xf3, 0xde, 0xf7, 0xde, 0xbc, - 0x79, 0xef, 0x7b, 0x06, 0x73, 0x1d, 0x84, 0x3c, 0xd5, 0x71, 0xed, 0x0e, 0x31, 0xb0, 0xab, 0x76, - 0x4a, 0xea, 0xd3, 0x36, 0x76, 0xbb, 0x8a, 0xe3, 0xda, 0xbe, 0x0d, 0x27, 0x83, 0x5d, 0x45, 0xec, - 0x2a, 0x9d, 0x52, 0x61, 0xae, 0x69, 0xdb, 0x4d, 0x13, 0xab, 0xc8, 0x21, 0x2a, 0xb2, 0x2c, 0xdb, - 0x47, 0x3e, 0xb1, 0x2d, 0x8f, 0xc9, 0x17, 0xa6, 0x9a, 0x76, 0xd3, 0xa6, 0x3f, 0xd5, 0xe0, 0x17, - 0x5f, 0x2d, 0xf2, 0x33, 0xf4, 0xab, 0xde, 0xde, 0x51, 0x7d, 0xd2, 0xc2, 0x9e, 0x8f, 0x5a, 0x8e, - 0x10, 0x38, 0x06, 0x22, 0x34, 0xc9, 0x04, 0xe6, 0xa9, 0x40, 0xa7, 0xa4, 0x7a, 0xbb, 0xc8, 0xc5, - 0x86, 0xde, 0xb0, 0x2d, 0xaf, 0xdd, 0x0a, 0xb7, 0xa1, 0xd8, 0x7e, 0x46, 0x5c, 0xcc, 0xd7, 0xe6, - 0x7c, 0x6c, 0x19, 0xd8, 0x6d, 0x11, 0xcb, 0x57, 0x1b, 0x6e, 0xd7, 0xf1, 0x6d, 0x75, 0x0f, 0x77, - 0x05, 0xd0, 0xd9, 0x86, 0xed, 0xb5, 0x6c, 0x4f, 0x67, 0x58, 0xd9, 0x07, 0xdf, 0xba, 0xc2, 0xbe, - 0x54, 0xcf, 0x47, 0x7b, 0xc4, 0x6a, 0xaa, 0x9d, 0x52, 0x1d, 0xfb, 0xa8, 0x24, 0xbe, 0xb9, 0xd4, - 0x0d, 0x2e, 0x55, 0x47, 0x1e, 0x66, 0x21, 0x0b, 0x05, 0x1d, 0xd4, 0x24, 0x16, 0x0d, 0x0b, 0x93, - 0x95, 0xbf, 0x0d, 0x2e, 0xfc, 0x30, 0x90, 0x58, 0xe7, 0xa8, 0x37, 0xb1, 0x85, 0x3d, 0xe2, 0x69, - 0xf8, 0x69, 0x1b, 0x7b, 0x3e, 0x2c, 0x82, 0x9c, 0xf0, 0x47, 0x27, 0x46, 0x5e, 0xba, 0x28, 0x2d, - 0x8e, 0x68, 0x40, 0x2c, 0x55, 0x0d, 0x79, 0x17, 0xcc, 0x25, 0x9f, 0xf7, 0x1c, 0xdb, 0xf2, 0x30, - 0xfc, 0x1e, 0x38, 0xd7, 0x64, 0x4b, 0xba, 0xe7, 0x23, 0x1f, 0x53, 0x15, 0xb9, 0xf2, 0xbc, 0x42, - 0x6f, 0xaf, 0x53, 0x52, 0x62, 0x07, 0xb7, 0x02, 0xa1, 0xca, 0xc8, 0xeb, 0xc3, 0xe2, 0x90, 0x76, - 0xb6, 0xd9, 0xb3, 0x26, 0xff, 0x4e, 0x02, 0x85, 0x88, 0xa9, 0xf5, 0x5d, 0x44, 0xac, 0x10, 0xe9, - 0x0a, 0x18, 0x75, 0x76, 0x91, 0xc7, 0x0c, 0x4c, 0x94, 0x8b, 0x4a, 0x3c, 0x3d, 0x42, 0x4b, 0xb5, - 0x40, 0x4c, 0x63, 0xd2, 0x70, 0x03, 0x80, 0xa3, 0x98, 0xe4, 0x33, 0x14, 0xdc, 0x35, 0x85, 0x07, - 0x3d, 0x08, 0xa0, 0xc2, 0x72, 0x8e, 0x07, 0x50, 0xa9, 0xa1, 0x26, 0xe6, 0x26, 0xb5, 0x9e, 0x93, - 0xf2, 0x6f, 0xa5, 0x58, 0x20, 0x05, 0x3a, 0x1e, 0x07, 0x15, 0x8c, 0x35, 0xe8, 0x4a, 0x5e, 0xba, - 0x38, 0xbc, 0x98, 0x2b, 0xcf, 0x24, 0xe0, 0x0b, 0xf6, 0x35, 0x2e, 0x06, 0x37, 0x13, 0x80, 0x7d, - 0xbd, 0x2f, 0x30, 0x66, 0x2d, 0x82, 0xec, 0xaf, 0x19, 0x30, 0x4a, 0x55, 0xc3, 0x59, 0x90, 0xa5, - 0xca, 0xc5, 0x4d, 0x8e, 0x6b, 0x67, 0xe8, 0x77, 0xd5, 0x80, 0x17, 0xc0, 0x78, 0xc3, 0x24, 0xd8, - 0xf2, 0x83, 0xbd, 0x0c, 0xdd, 0xcb, 0xb2, 0x85, 0xaa, 0x01, 0xa7, 0x44, 0x68, 0x87, 0xe9, 0x06, - 0x8f, 0xdc, 0x7d, 0x90, 0x6d, 0x61, 0x1f, 0x19, 0xc8, 0x47, 0xf9, 0x11, 0x0a, 0x4f, 0x4e, 0x8f, - 0xf9, 0x43, 0x2e, 0xc9, 0x6f, 0x36, 0x3c, 0x19, 0x4f, 0xb0, 0xd1, 0x78, 0x82, 0xc1, 0x1f, 0x83, - 0x69, 0x62, 0xed, 0xb8, 0xa8, 0x11, 0x38, 0xa3, 0x3b, 0xc8, 0x45, 0x2d, 0xec, 0x63, 0xd7, 0xcb, - 0x8f, 0xf1, 0xbb, 0x3a, 0x66, 0xb3, 0x1a, 0x8a, 0xd7, 0x42, 0x69, 0x6d, 0x8a, 0x24, 0xac, 0xc2, - 0x45, 0x30, 0xb9, 0x83, 0xb1, 0xee, 0xd8, 0xb6, 0xa9, 0x23, 0xc3, 0x70, 0xb1, 0xe7, 0xe5, 0xcf, - 0x50, 0x27, 0x27, 0x76, 0x30, 0xae, 0xd9, 0xb6, 0xb9, 0xc6, 0x56, 0xe5, 0x5f, 0x4b, 0xe0, 0x12, - 0xbd, 0xdf, 0xc7, 0xc8, 0x24, 0x06, 0xf2, 0x6d, 0x57, 0xb8, 0x16, 0x48, 0x88, 0x24, 0xfc, 0x16, - 0x98, 0x14, 0x48, 0x42, 0x7d, 0x34, 0xd2, 0x15, 0xf8, 0xe9, 0xb0, 0x38, 0xd1, 0x45, 0x2d, 0x73, - 0x55, 0xe6, 0x1b, 0xb2, 0x76, 0x5e, 0xc8, 0x72, 0x23, 0xf1, 0x60, 0x64, 0xe2, 0xc1, 0x58, 0xcd, - 0xfe, 0xf2, 0x65, 0x71, 0xe8, 0x5f, 0x2f, 0x8b, 0x43, 0xf2, 0x23, 0x20, 0x9f, 0x04, 0x87, 0x67, - 0xdd, 0x75, 0x30, 0x19, 0x2a, 0x8c, 0xe0, 0xd1, 0xce, 0x37, 0x7a, 0xe4, 0x93, 0x1d, 0xac, 0xf5, - 0xa0, 0xeb, 0x71, 0x30, 0x59, 0x61, 0xb2, 0x83, 0x31, 0x23, 0x5f, 0xe4, 0x60, 0x14, 0xce, 0x91, - 0x83, 0xc9, 0x01, 0x3f, 0x16, 0x5c, 0xf9, 0xfb, 0xe0, 0x3a, 0x55, 0xb8, 0x66, 0x9a, 0x35, 0x44, - 0x5c, 0xef, 0x31, 0x32, 0x83, 0x98, 0x05, 0xdb, 0x95, 0xf0, 0xd5, 0x0e, 0x5c, 0xf7, 0x7e, 0x25, - 0x81, 0x1b, 0x83, 0xa8, 0xe3, 0x38, 0x7f, 0x0a, 0xbe, 0xe2, 0x20, 0xe2, 0xea, 0x1d, 0x64, 0x06, - 0x0d, 0x82, 0x62, 0xe5, 0x95, 0x60, 0xf9, 0x78, 0x06, 0x07, 0x0a, 0x99, 0xbe, 0x40, 0x5d, 0xe8, - 0xb8, 0x65, 0x84, 0x7a, 0x27, 0x9c, 0x88, 0x88, 0xfc, 0x6f, 0x09, 0x5c, 0xea, 0x7b, 0x0a, 0x6e, - 0xa4, 0xa6, 0xe7, 0x85, 0x4f, 0x87, 0xc5, 0x19, 0x76, 0x7b, 0x71, 0x89, 0x84, 0x3c, 0xdd, 0x48, - 0xc8, 0x82, 0x4c, 0x5c, 0x4f, 0x5c, 0x22, 0x21, 0x1d, 0xbe, 0x03, 0xce, 0x86, 0x52, 0x7b, 0xb8, - 0x4b, 0xeb, 0x4b, 0xae, 0x3c, 0xa7, 0x1c, 0xb5, 0x47, 0x85, 0xb5, 0x47, 0xa5, 0xd6, 0xae, 0x9b, - 0xa4, 0xf1, 0x00, 0x77, 0xb5, 0xf0, 0x5e, 0x1e, 0xe0, 0xae, 0x3c, 0x05, 0x20, 0xbd, 0x04, 0xfa, - 0xa4, 0x45, 0x2b, 0x90, 0x1f, 0x82, 0xaf, 0x46, 0x56, 0xf9, 0x1d, 0xdc, 0x05, 0x63, 0xb4, 0x7c, - 0x78, 0xbc, 0x07, 0xe5, 0x93, 0x02, 0x1f, 0xec, 0xf3, 0x22, 0xc5, 0xa5, 0xe5, 0x35, 0xb0, 0x10, - 0xa9, 0xec, 0x61, 0x46, 0x0e, 0xde, 0x25, 0xff, 0x3b, 0x02, 0x2e, 0xa6, 0xe8, 0x08, 0x7f, 0x7d, - 0x69, 0xf1, 0x88, 0x07, 0x33, 0x73, 0xca, 0x60, 0xc2, 0xab, 0x60, 0x22, 0x54, 0xe0, 0xd8, 0xcf, - 0xb0, 0x4b, 0xef, 0x63, 0x58, 0x3b, 0x27, 0x56, 0x6b, 0xc1, 0x22, 0x7c, 0x00, 0x72, 0x06, 0xf6, - 0x1a, 0x2e, 0x71, 0x68, 0x67, 0x62, 0xa5, 0xff, 0xb2, 0xe8, 0x4c, 0x82, 0x89, 0x88, 0xb6, 0x74, - 0xff, 0x48, 0x94, 0x87, 0xb5, 0xf7, 0x34, 0xfc, 0x09, 0x98, 0x0d, 0x7d, 0xb6, 0x1d, 0xec, 0x06, - 0x81, 0x08, 0x9d, 0x1f, 0xa5, 0xce, 0x5f, 0x7a, 0xfb, 0x6a, 0x69, 0x9e, 0x6b, 0x0f, 0x83, 0xc5, - 0x9d, 0xde, 0xf2, 0x5d, 0x62, 0x35, 0xb5, 0x19, 0xa1, 0xe3, 0x11, 0x57, 0x21, 0x62, 0xf2, 0x35, - 0x30, 0xf6, 0x33, 0x44, 0x4c, 0x6c, 0xd0, 0x6e, 0x91, 0xd5, 0xf8, 0x17, 0x5c, 0x05, 0x63, 0x01, - 0x1b, 0x69, 0xb3, 0x6a, 0x3f, 0x51, 0x96, 0xd3, 0xe0, 0x57, 0x6c, 0xcb, 0xd8, 0xa2, 0x92, 0x1a, - 0x3f, 0x01, 0xb7, 0x41, 0x18, 0x7a, 0xdd, 0xb7, 0xf7, 0xb0, 0xe5, 0xe5, 0xb3, 0x14, 0xe8, 0xcd, - 0xc0, 0xbd, 0xbf, 0x1d, 0x16, 0xa7, 0x99, 0x2e, 0xcf, 0xd8, 0x53, 0x88, 0xad, 0xb6, 0x90, 0xbf, - 0xab, 0x54, 0x2d, 0xff, 0xed, 0xab, 0x25, 0xc0, 0x8d, 0x54, 0x2d, 0x5f, 0x9b, 0x10, 0x3a, 0xb6, - 0xa9, 0x8a, 0x20, 0xf8, 0xa1, 0x56, 0x16, 0xfc, 0x71, 0x16, 0x7c, 0xb1, 0xca, 0x82, 0x7f, 0x17, - 0xcc, 0x74, 0x58, 0x0c, 0xb0, 0xa7, 0x37, 0xda, 0xae, 0x1b, 0xb4, 0x6c, 0xec, 0xd8, 0x8d, 0xdd, - 0x3c, 0xa0, 0x1e, 0x4e, 0x87, 0xdb, 0xeb, 0x6c, 0xf7, 0xbb, 0xc1, 0xa6, 0xdc, 0x06, 0xc5, 0xd4, - 0x1c, 0xe6, 0xcf, 0x43, 0x03, 0xa0, 0x13, 0xae, 0xf2, 0xda, 0x54, 0x3e, 0xfe, 0x44, 0xfa, 0xa5, - 0xb1, 0xd6, 0xa3, 0x45, 0x96, 0x79, 0xda, 0x57, 0x4c, 0xbb, 0xb1, 0xe7, 0xfd, 0xc8, 0xf2, 0x89, - 0xf9, 0x03, 0xfc, 0x9c, 0x61, 0x12, 0xaf, 0xf5, 0x09, 0xef, 0x3b, 0xc9, 0x32, 0x1c, 0xdc, 0x0a, - 0x98, 0xa9, 0xd3, 0x7d, 0xbd, 0x1d, 0x08, 0xe8, 0x16, 0x7e, 0x2e, 0xfc, 0x66, 0xaf, 0x6d, 0xaa, - 0x9e, 0x70, 0x5c, 0x5e, 0xe3, 0x4d, 0x64, 0x3d, 0x7c, 0x8a, 0x1b, 0xae, 0xdd, 0x5a, 0xe7, 0xc4, - 0x46, 0x3c, 0xdf, 0x08, 0xf9, 0x91, 0xa2, 0xe4, 0x47, 0xde, 0x00, 0x97, 0x4f, 0x54, 0xc1, 0x01, - 0xf6, 0x2d, 0x01, 0xf7, 0xc0, 0xec, 0x71, 0x7e, 0x38, 0x70, 0x01, 0x79, 0x3b, 0x9c, 0x44, 0x7e, - 0x07, 0xb6, 0x1e, 0xa1, 0x7e, 0x99, 0x28, 0xf5, 0xbb, 0x0c, 0xce, 0xd9, 0xcf, 0xac, 0x9e, 0x9a, - 0xc3, 0x58, 0xde, 0x59, 0xba, 0x28, 0x1e, 0x52, 0x48, 0x01, 0x47, 0xd2, 0x28, 0xe0, 0xe8, 0x67, - 0x53, 0xc0, 0x2d, 0x90, 0x23, 0x16, 0xf1, 0x75, 0x5e, 0x9c, 0x19, 0xaf, 0x2b, 0xa7, 0x2b, 0xaa, - 0x5a, 0xc4, 0x27, 0xc8, 0x24, 0x3f, 0x47, 0x31, 0x8e, 0x07, 0x02, 0x35, 0xac, 0x84, 0xa7, 0xd3, - 0xc6, 0x33, 0xff, 0x07, 0xda, 0x18, 0x49, 0x98, 0x6c, 0x8c, 0x2d, 0x27, 0x71, 0xca, 0xf1, 0x44, - 0x4e, 0x59, 0x89, 0x3d, 0x4a, 0x3e, 0x02, 0x6d, 0x93, 0x16, 0x1e, 0x38, 0x31, 0xf6, 0x62, 0x8d, - 0x25, 0xa2, 0x83, 0x67, 0xc7, 0x26, 0x10, 0x93, 0x94, 0x1e, 0x4c, 0xb7, 0xbc, 0xfd, 0x15, 0x14, - 0x36, 0xfa, 0x2a, 0x62, 0xf4, 0x55, 0xb6, 0xc5, 0xe8, 0x5b, 0xc9, 0x06, 0x57, 0xf4, 0xe2, 0xef, - 0x45, 0x49, 0xcb, 0x35, 0x8f, 0x14, 0x96, 0xff, 0x73, 0x1e, 0x8c, 0x52, 0x6b, 0xf0, 0x8f, 0x12, - 0x98, 0x4a, 0xb2, 0x0b, 0x97, 0xfa, 0x54, 0x8c, 0xe8, 0x7c, 0x59, 0x50, 0x06, 0x15, 0x67, 0xae, - 0xc8, 0x2b, 0xbf, 0x78, 0xf7, 0xcf, 0xdf, 0x64, 0x54, 0xb8, 0xa4, 0x46, 0xc7, 0xf2, 0x30, 0x48, - 0x1c, 0xae, 0xba, 0xdf, 0x13, 0xb6, 0x03, 0xf8, 0x7b, 0x89, 0x53, 0x82, 0xe8, 0x74, 0x06, 0x6f, - 0xf5, 0x31, 0x1f, 0x19, 0x31, 0x0b, 0x4b, 0x03, 0x4a, 0x73, 0xac, 0x0a, 0xc5, 0xba, 0x08, 0xaf, - 0xa5, 0x61, 0x65, 0x93, 0x9e, 0xba, 0x4f, 0x1f, 0xd3, 0x01, 0x7c, 0x2f, 0x06, 0xdc, 0x44, 0x4e, - 0x0f, 0x97, 0x53, 0xac, 0x9f, 0x34, 0x90, 0x14, 0xee, 0x9c, 0xee, 0x10, 0x47, 0xfe, 0x88, 0x22, - 0xaf, 0xc2, 0xcd, 0x18, 0xf2, 0xb0, 0xb2, 0xeb, 0x11, 0x72, 0x17, 0x0d, 0xb6, 0xba, 0x1f, 0x67, - 0x31, 0x49, 0xae, 0xf5, 0xb2, 0xf9, 0xfe, 0xae, 0x25, 0x8c, 0x22, 0xfd, 0x5d, 0x4b, 0x1a, 0x18, - 0x06, 0x70, 0x2d, 0x82, 0x3e, 0xee, 0x5a, 0x9c, 0xd4, 0x1e, 0xc0, 0x77, 0x12, 0xef, 0x31, 0x27, - 0x0e, 0x02, 0xf0, 0x9b, 0x29, 0x68, 0x07, 0x99, 0x46, 0x0a, 0xf7, 0x3e, 0xef, 0x30, 0x77, 0xb9, - 0x4c, 0x5d, 0xbe, 0x05, 0x6f, 0xc4, 0x5c, 0xe6, 0x2e, 0xe8, 0xc1, 0x28, 0x11, 0x7f, 0x30, 0x5d, - 0x90, 0xeb, 0xa1, 0xd0, 0xf0, 0x4a, 0x0a, 0x80, 0x08, 0xef, 0x2e, 0x5c, 0xed, 0x23, 0xc5, 0xf1, - 0xcc, 0x53, 0x3c, 0x33, 0x70, 0x3a, 0x86, 0x87, 0xd5, 0x7f, 0xf8, 0x27, 0x09, 0xcc, 0xa4, 0x90, - 0x0c, 0x78, 0x7b, 0x60, 0x3e, 0x22, 0x30, 0x95, 0x4e, 0x71, 0x82, 0xe3, 0xfb, 0x06, 0xc5, 0x57, - 0x86, 0xb7, 0xd3, 0xde, 0xed, 0x11, 0xc1, 0x89, 0x45, 0xed, 0x95, 0xc4, 0x9b, 0x7c, 0x12, 0x97, - 0x81, 0x69, 0x64, 0xea, 0x04, 0x72, 0x54, 0x58, 0x3e, 0xd5, 0x99, 0x3e, 0x85, 0x27, 0x85, 0x41, - 0xc1, 0x3f, 0xc7, 0xff, 0xbb, 0x8a, 0x72, 0x1c, 0x78, 0xa7, 0x4f, 0x0c, 0x13, 0x59, 0x55, 0x61, - 0xe5, 0x94, 0xa7, 0x06, 0xad, 0x9a, 0xc4, 0x50, 0xf7, 0xc3, 0xee, 0x7b, 0x00, 0xff, 0x20, 0xf1, - 0x19, 0x30, 0x52, 0x85, 0xe1, 0xcd, 0x41, 0x6a, 0xb5, 0x80, 0x7a, 0x6b, 0x30, 0x61, 0x8e, 0x70, - 0x99, 0x22, 0x5c, 0x82, 0x37, 0x4f, 0xac, 0xeb, 0xb1, 0xd4, 0xf8, 0x8b, 0x04, 0xf2, 0x69, 0x8d, - 0x1a, 0x96, 0x06, 0xeb, 0x82, 0x3d, 0xc4, 0xa0, 0x50, 0x3e, 0xcd, 0x11, 0x0e, 0x7c, 0x95, 0x02, - 0xbf, 0x03, 0xcb, 0x7d, 0x9a, 0x27, 0x65, 0x09, 0x51, 0xfc, 0x95, 0xea, 0xeb, 0x0f, 0x0b, 0xd2, - 0x9b, 0x0f, 0x0b, 0xd2, 0x3f, 0x3e, 0x2c, 0x48, 0x2f, 0x3e, 0x2e, 0x0c, 0xbd, 0xf9, 0xb8, 0x30, - 0xf4, 0xfe, 0xe3, 0xc2, 0xd0, 0x13, 0xb5, 0x49, 0xfc, 0xdd, 0x76, 0x5d, 0x69, 0xd8, 0x2d, 0x15, - 0x99, 0x26, 0xb1, 0xea, 0xc4, 0xf7, 0x98, 0x85, 0xe7, 0x31, 0x43, 0x7e, 0xd7, 0xc1, 0x5e, 0x7d, - 0x8c, 0x12, 0x8e, 0xe5, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x16, 0x20, 0x71, 0xbb, 0xdf, 0x17, + // 2034 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0x4d, 0x70, 0x1c, 0x47, + 0x15, 0xd6, 0xac, 0x25, 0x59, 0x6a, 0xd9, 0x8a, 0xe9, 0x48, 0x68, 0xb5, 0x96, 0xb5, 0xf6, 0x38, + 0x09, 0x8a, 0x6d, 0xcd, 0x58, 0x2b, 0xdb, 0x95, 0x12, 0x21, 0xe0, 0x95, 0x23, 0xb3, 0x65, 0x82, + 0xc5, 0xd8, 0xe4, 0x10, 0x0a, 0xa6, 0x7a, 0x77, 0xda, 0xab, 0x46, 0xb3, 0x33, 0x93, 0x99, 0xd9, + 0xb5, 0x17, 0x95, 0x2e, 0x1c, 0x28, 0x0e, 0x81, 0x4a, 0x15, 0x07, 0x0e, 0x5c, 0x52, 0x14, 0x27, + 0x7e, 0x6e, 0x3e, 0x50, 0x54, 0x71, 0x82, 0x43, 0x0e, 0x1c, 0x52, 0xce, 0x25, 0xc5, 0x41, 0xa1, + 0x6c, 0x0e, 0x9c, 0x53, 0x9c, 0x29, 0x6a, 0xfa, 0x4f, 0x3b, 0xad, 0x99, 0xdd, 0x51, 0xe2, 0x9c, + 0xb4, 0xd3, 0xfd, 0xfa, 0xf5, 0xf7, 0x5e, 0xbf, 0x7e, 0xef, 0x7b, 0x2d, 0xb0, 0xd4, 0x43, 0x28, + 0x32, 0x83, 0xd0, 0xef, 0x11, 0x07, 0x87, 0x66, 0x6f, 0xcd, 0x7c, 0xb7, 0x8b, 0xc3, 0xbe, 0x11, + 0x84, 0x7e, 0xec, 0xc3, 0x33, 0xc9, 0xac, 0x21, 0x66, 0x8d, 0xde, 0x5a, 0x65, 0xa9, 0xed, 0xfb, + 0x6d, 0x17, 0x9b, 0x28, 0x20, 0x26, 0xf2, 0x3c, 0x3f, 0x46, 0x31, 0xf1, 0xbd, 0x88, 0xc9, 0x57, + 0xe6, 0xda, 0x7e, 0xdb, 0xa7, 0x3f, 0xcd, 0xe4, 0x17, 0x1f, 0xad, 0xf2, 0x35, 0xf4, 0xab, 0xd9, + 0x7d, 0x60, 0xc6, 0xa4, 0x83, 0xa3, 0x18, 0x75, 0x02, 0x21, 0x70, 0x04, 0x84, 0xdc, 0x92, 0x09, + 0x9c, 0xa3, 0x02, 0xbd, 0x35, 0x33, 0xda, 0x41, 0x21, 0x76, 0xec, 0x96, 0xef, 0x45, 0xdd, 0x8e, + 0x9c, 0x86, 0x62, 0xfa, 0x21, 0x09, 0x31, 0x1f, 0x5b, 0x8a, 0xb1, 0xe7, 0xe0, 0xb0, 0x43, 0xbc, + 0xd8, 0x6c, 0x85, 0xfd, 0x20, 0xf6, 0xcd, 0x5d, 0xdc, 0x17, 0x40, 0x17, 0x5b, 0x7e, 0xd4, 0xf1, + 0x23, 0x9b, 0x61, 0x65, 0x1f, 0x7c, 0xea, 0x25, 0xf6, 0x65, 0x46, 0x31, 0xda, 0x25, 0x5e, 0xdb, + 0xec, 0xad, 0x35, 0x71, 0x8c, 0xd6, 0xc4, 0x37, 0x97, 0xba, 0xc4, 0xa5, 0x9a, 0x28, 0xc2, 0xcc, + 0x65, 0x52, 0x30, 0x40, 0x6d, 0xe2, 0x51, 0xb7, 0x70, 0xd9, 0xe5, 0x41, 0x59, 0x21, 0xd5, 0xf2, + 0x09, 0x9f, 0xd7, 0xdf, 0x00, 0x67, 0xbf, 0x97, 0x68, 0xd8, 0xe4, 0x56, 0xdd, 0xc6, 0x1e, 0x8e, + 0x48, 0x64, 0xe1, 0x77, 0xbb, 0x38, 0x8a, 0x61, 0x15, 0xcc, 0x08, 0x7b, 0x6d, 0xe2, 0x94, 0xb5, + 0xf3, 0xda, 0xca, 0xb8, 0x05, 0xc4, 0x50, 0xc3, 0xd1, 0x77, 0xc0, 0x52, 0xf6, 0xfa, 0x28, 0xf0, + 0xbd, 0x08, 0xc3, 0x6f, 0x83, 0xd3, 0x6d, 0x36, 0x64, 0x47, 0x31, 0x8a, 0x31, 0x55, 0x31, 0x53, + 0x3b, 0x67, 0xd0, 0xd3, 0xed, 0xad, 0x19, 0xca, 0xc2, 0x7b, 0x89, 0x50, 0x7d, 0xfc, 0xc3, 0x83, + 0xea, 0x98, 0x75, 0xaa, 0x3d, 0x30, 0xa6, 0xff, 0x46, 0x03, 0x95, 0xd4, 0x56, 0x9b, 0x3b, 0x88, + 0x78, 0x12, 0xe9, 0x75, 0x30, 0x11, 0xec, 0xa0, 0x88, 0x6d, 0x30, 0x5b, 0xab, 0x1a, 0x6a, 0xf8, + 0xc8, 0x9d, 0xb6, 0x13, 0x31, 0x8b, 0x49, 0xc3, 0x2d, 0x00, 0x0e, 0x7d, 0x56, 0x2e, 0x51, 0x70, + 0xaf, 0x18, 0xfc, 0x50, 0x12, 0xa7, 0x19, 0x2c, 0x26, 0xb9, 0xeb, 0x8c, 0x6d, 0xd4, 0xc6, 0x7c, + 0x4b, 0x6b, 0x60, 0xa5, 0xfe, 0x6b, 0x4d, 0x71, 0xa4, 0x40, 0xc7, 0xfd, 0x60, 0x82, 0xc9, 0x16, + 0x1d, 0x29, 0x6b, 0xe7, 0x4f, 0xac, 0xcc, 0xd4, 0x16, 0x32, 0xf0, 0x25, 0xf3, 0x16, 0x17, 0x83, + 0xb7, 0x33, 0x80, 0x7d, 0x6d, 0x24, 0x30, 0xb6, 0x5b, 0x0a, 0xd9, 0xdf, 0x4a, 0x60, 0x82, 0xaa, + 0x86, 0x8b, 0x60, 0x8a, 0x2a, 0x17, 0x27, 0x39, 0x6d, 0x9d, 0xa4, 0xdf, 0x0d, 0x07, 0x9e, 0x05, + 0xd3, 0x2d, 0x97, 0x60, 0x2f, 0x4e, 0xe6, 0x4a, 0x74, 0x6e, 0x8a, 0x0d, 0x34, 0x1c, 0x38, 0x27, + 0x5c, 0x7b, 0x82, 0x4e, 0x70, 0xcf, 0xdd, 0x02, 0x53, 0x1d, 0x1c, 0x23, 0x07, 0xc5, 0xa8, 0x3c, + 0x4e, 0xe1, 0xe9, 0xf9, 0x3e, 0x7f, 0x8b, 0x4b, 0xf2, 0x93, 0x95, 0x2b, 0xd5, 0x00, 0x9b, 0x50, + 0x03, 0x0c, 0xfe, 0x00, 0xcc, 0x13, 0xef, 0x41, 0x88, 0x5a, 0x89, 0x31, 0x76, 0x80, 0x42, 0xd4, + 0xc1, 0x31, 0x0e, 0xa3, 0xf2, 0x24, 0x3f, 0xab, 0x23, 0x7b, 0x36, 0xa4, 0xf8, 0xb6, 0x94, 0xb6, + 0xe6, 0x48, 0xc6, 0x28, 0x5c, 0x01, 0x67, 0x1e, 0x60, 0x6c, 0x07, 0xbe, 0xef, 0xda, 0xc8, 0x71, + 0x42, 0x1c, 0x45, 0xe5, 0x93, 0xd4, 0xc8, 0xd9, 0x07, 0x18, 0x6f, 0xfb, 0xbe, 0x7b, 0x93, 0x8d, + 0xea, 0xbf, 0xd0, 0xc0, 0x05, 0x7a, 0xbe, 0x6f, 0x23, 0x97, 0x38, 0x28, 0xf6, 0x43, 0x61, 0x5a, + 0x22, 0x21, 0x82, 0xf0, 0x1b, 0xe0, 0x8c, 0x40, 0x22, 0xf5, 0x51, 0x4f, 0xd7, 0xe1, 0x67, 0x07, + 0xd5, 0xd9, 0x3e, 0xea, 0xb8, 0x1b, 0x3a, 0x9f, 0xd0, 0xad, 0x17, 0x84, 0x2c, 0xdf, 0x44, 0x75, + 0x46, 0x49, 0x75, 0xc6, 0xc6, 0xd4, 0xcf, 0x3f, 0xa8, 0x8e, 0xfd, 0xe7, 0x83, 0xea, 0x98, 0x7e, + 0x17, 0xe8, 0xc3, 0xe0, 0xf0, 0xa8, 0x7b, 0x15, 0x9c, 0x91, 0x0a, 0x53, 0x78, 0xac, 0x17, 0x5a, + 0x03, 0xf2, 0xd9, 0x06, 0x6e, 0x0f, 0xa0, 0x1b, 0x30, 0x30, 0x5b, 0x61, 0xb6, 0x81, 0xca, 0x26, + 0x5f, 0xc8, 0xc0, 0x34, 0x9c, 0x43, 0x03, 0xb3, 0x1d, 0x7e, 0xc4, 0xb9, 0xfa, 0x77, 0xc0, 0xab, + 0x54, 0xe1, 0x4d, 0xd7, 0xdd, 0x46, 0x24, 0x8c, 0xde, 0x46, 0x6e, 0xe2, 0xb3, 0x64, 0xba, 0x2e, + 0x6f, 0x6d, 0xe1, 0xbc, 0xf7, 0x9e, 0x06, 0x2e, 0x15, 0x51, 0xc7, 0x71, 0xfe, 0x08, 0x7c, 0x25, + 0x40, 0x24, 0xb4, 0x7b, 0xc8, 0x4d, 0x0a, 0x08, 0xc5, 0xca, 0x33, 0xc1, 0xfa, 0xd1, 0x08, 0x4e, + 0x14, 0x32, 0x7d, 0x89, 0x3a, 0x69, 0xb8, 0xe7, 0x48, 0xbd, 0xb3, 0x41, 0x4a, 0x44, 0xff, 0xaf, + 0x06, 0x2e, 0x8c, 0x5c, 0x05, 0xb7, 0x72, 0xc3, 0xf3, 0xec, 0x67, 0x07, 0xd5, 0x05, 0x76, 0x7a, + 0xaa, 0x44, 0x46, 0x9c, 0x6e, 0x65, 0x44, 0x41, 0x49, 0xd5, 0xa3, 0x4a, 0x64, 0x84, 0xc3, 0x37, + 0xc1, 0x29, 0x29, 0xb5, 0x8b, 0xfb, 0x34, 0xbf, 0xcc, 0xd4, 0x96, 0x8c, 0xc3, 0xf2, 0x69, 0xb0, + 0xf2, 0x69, 0x6c, 0x77, 0x9b, 0x2e, 0x69, 0xdd, 0xc1, 0x7d, 0x4b, 0x9e, 0xcb, 0x1d, 0xdc, 0xd7, + 0xe7, 0x00, 0xa4, 0x87, 0x40, 0xaf, 0xb4, 0x28, 0x05, 0xfa, 0x5b, 0xe0, 0xc5, 0xd4, 0x28, 0x3f, + 0x83, 0x1b, 0x60, 0x92, 0xa6, 0x8f, 0x88, 0xd7, 0xa0, 0x72, 0x96, 0xe3, 0x93, 0x79, 0x9e, 0xa4, + 0xb8, 0xb4, 0x7e, 0x13, 0x2c, 0xa7, 0x32, 0xbb, 0x8c, 0xc8, 0xe2, 0x55, 0xf2, 0x7f, 0xe3, 0xe0, + 0x7c, 0x8e, 0x0e, 0xf9, 0xeb, 0x8b, 0x26, 0x0f, 0xd5, 0x99, 0xa5, 0x63, 0x3a, 0x13, 0xbe, 0x0c, + 0x66, 0xa5, 0x82, 0xc0, 0x7f, 0x88, 0x43, 0x7a, 0x1e, 0x27, 0xac, 0xd3, 0x62, 0x74, 0x3b, 0x19, + 0x84, 0x77, 0xc0, 0x8c, 0x83, 0xa3, 0x56, 0x48, 0x02, 0x5a, 0x99, 0x58, 0xea, 0xbf, 0x28, 0x2a, + 0x93, 0x60, 0x2a, 0xa2, 0x2c, 0xdd, 0x3a, 0x14, 0xe5, 0x6e, 0x1d, 0x5c, 0x0d, 0x7f, 0x08, 0x16, + 0xa5, 0xcd, 0x7e, 0x80, 0xc3, 0xc4, 0x11, 0xd2, 0xf8, 0x09, 0x6a, 0xfc, 0x85, 0x27, 0x8f, 0x57, + 0xcf, 0x71, 0xed, 0xd2, 0x59, 0xdc, 0xe8, 0x7b, 0x71, 0x48, 0xbc, 0xb6, 0xb5, 0x20, 0x74, 0xdc, + 0xe5, 0x2a, 0x84, 0x4f, 0xbe, 0x0a, 0x26, 0x7f, 0x8c, 0x88, 0x8b, 0x1d, 0x5a, 0x2d, 0xa6, 0x2c, + 0xfe, 0x05, 0x37, 0xc0, 0x64, 0xc2, 0x46, 0xba, 0x2c, 0xdb, 0xcf, 0xd6, 0xf4, 0x3c, 0xf8, 0x75, + 0xdf, 0x73, 0xee, 0x51, 0x49, 0x8b, 0xaf, 0x80, 0xf7, 0x81, 0x74, 0xbd, 0x1d, 0xfb, 0xbb, 0xd8, + 0x8b, 0xca, 0x53, 0x14, 0xe8, 0xe5, 0xc4, 0xbc, 0x7f, 0x1e, 0x54, 0xe7, 0x99, 0xae, 0xc8, 0xd9, + 0x35, 0x88, 0x6f, 0x76, 0x50, 0xbc, 0x63, 0x34, 0xbc, 0xf8, 0xc9, 0xe3, 0x55, 0xc0, 0x37, 0x69, + 0x78, 0xb1, 0x35, 0x2b, 0x74, 0xdc, 0xa7, 0x2a, 0x12, 0xe7, 0x4b, 0xad, 0xcc, 0xf9, 0xd3, 0xcc, + 0xf9, 0x62, 0x94, 0x39, 0xff, 0x06, 0x58, 0xe8, 0x31, 0x1f, 0xe0, 0xc8, 0x6e, 0x75, 0xc3, 0x30, + 0x29, 0xd9, 0x38, 0xf0, 0x5b, 0x3b, 0x65, 0x40, 0x2d, 0x9c, 0x97, 0xd3, 0x9b, 0x6c, 0xf6, 0xcd, + 0x64, 0x52, 0xef, 0x82, 0x6a, 0x6e, 0x0c, 0xf3, 0xeb, 0x61, 0x01, 0xd0, 0x93, 0xa3, 0x3c, 0x37, + 0xd5, 0x8e, 0x5e, 0x91, 0x51, 0x61, 0x6c, 0x0d, 0x68, 0xd1, 0x75, 0x1e, 0xf6, 0x75, 0xd7, 0x6f, + 0xed, 0x46, 0xdf, 0xf7, 0x62, 0xe2, 0x7e, 0x17, 0x3f, 0x62, 0x98, 0xc4, 0x6d, 0x7d, 0x87, 0xd7, + 0x9d, 0x6c, 0x19, 0x0e, 0xee, 0x3a, 0x58, 0x68, 0xd2, 0x79, 0xbb, 0x9b, 0x08, 0xd8, 0x1e, 0x7e, + 0x24, 0xec, 0x66, 0xb7, 0x6d, 0xae, 0x99, 0xb1, 0x5c, 0xbf, 0xc9, 0x8b, 0xc8, 0xa6, 0xbc, 0x8a, + 0x5b, 0xa1, 0xdf, 0xd9, 0xe4, 0xc4, 0x46, 0x5c, 0xdf, 0x14, 0xf9, 0xd1, 0xd2, 0xe4, 0x47, 0xdf, + 0x02, 0x17, 0x87, 0xaa, 0xe0, 0x00, 0x47, 0xa6, 0x80, 0xd7, 0xc1, 0xe2, 0x51, 0x7e, 0x58, 0x38, + 0x81, 0x3c, 0x39, 0x91, 0x45, 0x7e, 0x0b, 0xef, 0x9e, 0xa2, 0x7e, 0xa5, 0x34, 0xf5, 0xbb, 0x08, + 0x4e, 0xfb, 0x0f, 0xbd, 0x81, 0x9c, 0xc3, 0x58, 0xde, 0x29, 0x3a, 0x28, 0x2e, 0x92, 0xa4, 0x80, + 0xe3, 0x79, 0x14, 0x70, 0xe2, 0x73, 0x53, 0xc0, 0x7b, 0x60, 0x86, 0x78, 0x24, 0xb6, 0x79, 0x72, + 0x66, 0xbc, 0xae, 0x96, 0xaf, 0xa8, 0xe1, 0x91, 0x98, 0x20, 0x97, 0xfc, 0x04, 0x29, 0x1c, 0x0f, + 0x24, 0x6a, 0x58, 0x0a, 0xcf, 0xa7, 0x8d, 0x27, 0x9f, 0x03, 0x6d, 0x4c, 0x05, 0xcc, 0x94, 0xc2, + 0x96, 0xb3, 0x38, 0xe5, 0x74, 0x26, 0xa7, 0xac, 0x2b, 0x97, 0x92, 0xb7, 0x40, 0xf7, 0x49, 0x07, + 0x17, 0x0e, 0x8c, 0x5d, 0xa5, 0xb0, 0xa4, 0x74, 0xf0, 0xe8, 0xb8, 0x0d, 0x44, 0x27, 0x65, 0x27, + 0xdd, 0x2f, 0x2f, 0x7f, 0x15, 0x83, 0xb5, 0xc6, 0x86, 0x68, 0x8d, 0x8d, 0xfb, 0xa2, 0x35, 0xae, + 0x4f, 0x25, 0x47, 0xf4, 0xfe, 0xa7, 0x55, 0xcd, 0x9a, 0x69, 0x1f, 0x2a, 0xd4, 0xf7, 0x94, 0xcd, + 0xb6, 0x98, 0x3d, 0x9b, 0x2e, 0x22, 0x9d, 0xa2, 0x88, 0xe1, 0x0d, 0x30, 0xed, 0xe0, 0xc0, 0x8f, + 0x48, 0xec, 0x87, 0x9c, 0x35, 0x94, 0x9f, 0x3c, 0x5e, 0x9d, 0xe3, 0xc9, 0x31, 0x9d, 0xd9, 0x0f, + 0x45, 0xf5, 0x9f, 0x09, 0x82, 0x9a, 0xbd, 0x3b, 0xb7, 0x15, 0x81, 0x89, 0x56, 0x32, 0xc0, 0x13, + 0xd8, 0x62, 0xaa, 0x63, 0x12, 0x59, 0x7d, 0xd3, 0x27, 0x5e, 0xfd, 0x6a, 0x62, 0xe3, 0xef, 0x3f, + 0xad, 0xae, 0xb4, 0x49, 0xbc, 0xd3, 0x6d, 0x1a, 0x2d, 0xbf, 0xc3, 0x9b, 0x71, 0xfe, 0x67, 0x35, + 0x72, 0x76, 0xcd, 0xb8, 0x1f, 0xe0, 0x88, 0x2e, 0x88, 0x2c, 0xa6, 0x59, 0xff, 0x83, 0x06, 0x66, + 0x6f, 0x09, 0x58, 0x74, 0xf7, 0xb4, 0x4d, 0x5a, 0x61, 0x9b, 0x0e, 0xd1, 0x96, 0xbe, 0x34, 0xb4, + 0xef, 0x0d, 0x73, 0x5b, 0x61, 0x06, 0xf3, 0xdc, 0xfa, 0xe4, 0x3f, 0x69, 0x4a, 0x4a, 0x56, 0xe0, + 0xf0, 0x63, 0x7c, 0x03, 0x4c, 0x52, 0xf8, 0xa2, 0x10, 0x9d, 0x3f, 0x7a, 0x5f, 0xd3, 0x47, 0x20, + 0x38, 0x1b, 0x5b, 0xf5, 0xdc, 0xba, 0xe7, 0xda, 0x2f, 0x5f, 0x04, 0x13, 0x14, 0x2f, 0xfc, 0xa3, + 0x06, 0xe6, 0xb2, 0xae, 0x1a, 0x5c, 0x1d, 0x51, 0x24, 0xd3, 0x4f, 0x2a, 0x15, 0xa3, 0xa8, 0x38, + 0x43, 0xa3, 0x5f, 0xff, 0xe9, 0xc7, 0xff, 0xfe, 0x55, 0xc9, 0x84, 0xab, 0x66, 0xfa, 0xa5, 0x4a, + 0x9e, 0x17, 0xbf, 0xa1, 0xe6, 0xde, 0xc0, 0x09, 0xee, 0xc3, 0xdf, 0x6a, 0x9c, 0x05, 0xa7, 0x1f, + 0x24, 0xe0, 0x95, 0x11, 0xdb, 0xa7, 0x5e, 0x55, 0x2a, 0xab, 0x05, 0xa5, 0x39, 0x56, 0x83, 0x62, + 0x5d, 0x81, 0xaf, 0xe4, 0x61, 0x65, 0x8f, 0x1b, 0xe6, 0x1e, 0xad, 0x1f, 0xfb, 0xf0, 0x13, 0xf1, + 0xa6, 0x93, 0xd9, 0xc6, 0xc2, 0xf5, 0x9c, 0xdd, 0x87, 0xf5, 0xe0, 0x95, 0x6b, 0xc7, 0x5b, 0xc4, + 0x91, 0xdf, 0xa5, 0xc8, 0x1b, 0xf0, 0xb6, 0x82, 0x5c, 0x92, 0x19, 0x3b, 0xd5, 0xcf, 0xa4, 0x9d, + 0x6d, 0xee, 0xa9, 0xc4, 0x3d, 0xcb, 0xb4, 0xc1, 0x06, 0x76, 0xb4, 0x69, 0x19, 0xdd, 0xf7, 0x68, + 0xd3, 0xb2, 0x7a, 0xe4, 0x02, 0xa6, 0xa5, 0xd0, 0xab, 0xa6, 0xa9, 0x7d, 0xdc, 0x3e, 0xfc, 0x58, + 0xdc, 0xe1, 0xa1, 0xbd, 0x2f, 0xfc, 0x7a, 0x0e, 0xda, 0x22, 0x0d, 0x78, 0xe5, 0xf5, 0xcf, 0xb7, + 0x98, 0x9b, 0x5c, 0xa3, 0x26, 0x5f, 0x81, 0x97, 0x14, 0x93, 0xb9, 0x09, 0x76, 0xd2, 0x3d, 0xab, + 0x17, 0xa6, 0x0f, 0x66, 0x06, 0xba, 0x46, 0xf8, 0x52, 0x0e, 0x80, 0x54, 0xab, 0x59, 0x79, 0x79, + 0x84, 0x14, 0xc7, 0x73, 0x8e, 0xe2, 0x59, 0x80, 0xf3, 0x0a, 0x1e, 0x46, 0x79, 0xe0, 0x9f, 0x35, + 0xb0, 0x90, 0xc3, 0xab, 0xe1, 0xd5, 0xc2, 0x14, 0x5c, 0x60, 0x5a, 0x3b, 0xc6, 0x0a, 0x8e, 0xef, + 0x35, 0x8a, 0xaf, 0x06, 0xaf, 0xe6, 0xdd, 0xdb, 0x43, 0x4e, 0xaf, 0x78, 0xed, 0xb1, 0xc6, 0x79, + 0x6d, 0x16, 0x7d, 0x87, 0x79, 0xfd, 0xc3, 0x90, 0x7e, 0xa0, 0xb2, 0x7e, 0xac, 0x35, 0x23, 0x12, + 0x4f, 0x4e, 0xd3, 0x00, 0xff, 0xa2, 0x3e, 0xd7, 0xa6, 0x69, 0x3d, 0xbc, 0x36, 0xc2, 0x87, 0x99, + 0x8d, 0x44, 0xe5, 0xfa, 0x31, 0x57, 0x15, 0xcd, 0x9a, 0xc4, 0x31, 0xf7, 0x24, 0xe1, 0xdc, 0x87, + 0xbf, 0xd3, 0xf8, 0xb3, 0x47, 0x2a, 0x0b, 0xc3, 0xcb, 0x45, 0x72, 0xb5, 0x80, 0x7a, 0xa5, 0x98, + 0x30, 0x47, 0xb8, 0x4e, 0x11, 0xae, 0xc2, 0xcb, 0x43, 0xf3, 0xba, 0x12, 0x1a, 0x7f, 0xd5, 0x40, + 0x39, 0x8f, 0x9b, 0xc2, 0xb5, 0x62, 0x55, 0x70, 0x80, 0x0b, 0x57, 0x6a, 0xc7, 0x59, 0xc2, 0x81, + 0x6f, 0x50, 0xe0, 0xd7, 0x60, 0x6d, 0x44, 0xf1, 0xa4, 0xc4, 0x58, 0xc1, 0xff, 0x0f, 0x0d, 0xcc, + 0x65, 0xb1, 0x14, 0x38, 0x0a, 0x48, 0x06, 0x2d, 0xce, 0x8d, 0xea, 0x61, 0x64, 0x56, 0xbf, 0x43, + 0xd1, 0xbf, 0x09, 0x37, 0xcd, 0x23, 0xff, 0xa4, 0x92, 0x48, 0x65, 0xaf, 0x41, 0x99, 0x8f, 0x9a, + 0xbc, 0x25, 0xd5, 0xdc, 0x87, 0x7f, 0xd7, 0xc0, 0x7c, 0x26, 0xe9, 0x82, 0xc7, 0xc1, 0x16, 0x8d, + 0xaa, 0x45, 0x43, 0x79, 0x9d, 0xfe, 0x2d, 0x6a, 0xd1, 0x06, 0x7c, 0xad, 0xb0, 0x45, 0x4a, 0xc2, + 0xa9, 0x37, 0x3e, 0x7c, 0xba, 0xac, 0x7d, 0xf4, 0x74, 0x59, 0xfb, 0xd7, 0xd3, 0x65, 0xed, 0xfd, + 0x67, 0xcb, 0x63, 0x1f, 0x3d, 0x5b, 0x1e, 0xfb, 0xe4, 0xd9, 0xf2, 0xd8, 0x3b, 0xe6, 0x00, 0x35, + 0x46, 0xae, 0x4b, 0xbc, 0x26, 0x89, 0x23, 0xb6, 0xcf, 0x23, 0x65, 0x3b, 0xca, 0x93, 0x9b, 0x93, + 0xb4, 0xf3, 0x59, 0xff, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4a, 0x42, 0xd0, 0x46, 0x88, 0x1c, 0x00, 0x00, } @@ -1553,6 +1830,8 @@ type QueryClient interface { // QueryConsumerGenesisTime returns the genesis time // of the consumer chain associated with the provided consumer id QueryConsumerGenesisTime(ctx context.Context, in *QueryConsumerGenesisTimeRequest, opts ...grpc.CallOption) (*QueryConsumerGenesisTimeResponse, error) + ConsumerFeePoolClaim(ctx context.Context, in *QueryConsumerFeePoolClaimRequest, opts ...grpc.CallOption) (*QueryConsumerFeePoolClaimResponse, error) + ConsumerFeePoolClaims(ctx context.Context, in *QueryConsumerFeePoolClaimsRequest, opts ...grpc.CallOption) (*QueryConsumerFeePoolClaimsResponse, error) } type queryClient struct { @@ -1662,6 +1941,24 @@ func (c *queryClient) QueryConsumerGenesisTime(ctx context.Context, in *QueryCon return out, nil } +func (c *queryClient) ConsumerFeePoolClaim(ctx context.Context, in *QueryConsumerFeePoolClaimRequest, opts ...grpc.CallOption) (*QueryConsumerFeePoolClaimResponse, error) { + out := new(QueryConsumerFeePoolClaimResponse) + err := c.cc.Invoke(ctx, "/vaas.provider.v1.Query/ConsumerFeePoolClaim", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ConsumerFeePoolClaims(ctx context.Context, in *QueryConsumerFeePoolClaimsRequest, opts ...grpc.CallOption) (*QueryConsumerFeePoolClaimsResponse, error) { + out := new(QueryConsumerFeePoolClaimsResponse) + err := c.cc.Invoke(ctx, "/vaas.provider.v1.Query/ConsumerFeePoolClaims", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // ConsumerGenesis queries the genesis state needed to start a consumer chain @@ -1697,6 +1994,8 @@ type QueryServer interface { // QueryConsumerGenesisTime returns the genesis time // of the consumer chain associated with the provided consumer id QueryConsumerGenesisTime(context.Context, *QueryConsumerGenesisTimeRequest) (*QueryConsumerGenesisTimeResponse, error) + ConsumerFeePoolClaim(context.Context, *QueryConsumerFeePoolClaimRequest) (*QueryConsumerFeePoolClaimResponse, error) + ConsumerFeePoolClaims(context.Context, *QueryConsumerFeePoolClaimsRequest) (*QueryConsumerFeePoolClaimsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -1736,6 +2035,12 @@ func (*UnimplementedQueryServer) QueryConsumerChain(ctx context.Context, req *Qu func (*UnimplementedQueryServer) QueryConsumerGenesisTime(ctx context.Context, req *QueryConsumerGenesisTimeRequest) (*QueryConsumerGenesisTimeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryConsumerGenesisTime not implemented") } +func (*UnimplementedQueryServer) ConsumerFeePoolClaim(ctx context.Context, req *QueryConsumerFeePoolClaimRequest) (*QueryConsumerFeePoolClaimResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConsumerFeePoolClaim not implemented") +} +func (*UnimplementedQueryServer) ConsumerFeePoolClaims(ctx context.Context, req *QueryConsumerFeePoolClaimsRequest) (*QueryConsumerFeePoolClaimsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConsumerFeePoolClaims not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -1939,6 +2244,42 @@ func _Query_QueryConsumerGenesisTime_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _Query_ConsumerFeePoolClaim_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConsumerFeePoolClaimRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConsumerFeePoolClaim(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vaas.provider.v1.Query/ConsumerFeePoolClaim", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConsumerFeePoolClaim(ctx, req.(*QueryConsumerFeePoolClaimRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ConsumerFeePoolClaims_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConsumerFeePoolClaimsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConsumerFeePoolClaims(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vaas.provider.v1.Query/ConsumerFeePoolClaims", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConsumerFeePoolClaims(ctx, req.(*QueryConsumerFeePoolClaimsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "vaas.provider.v1.Query", HandlerType: (*QueryServer)(nil), @@ -1987,6 +2328,14 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryConsumerGenesisTime", Handler: _Query_QueryConsumerGenesisTime_Handler, }, + { + MethodName: "ConsumerFeePoolClaim", + Handler: _Query_ConsumerFeePoolClaim_Handler, + }, + { + MethodName: "ConsumerFeePoolClaims", + Handler: _Query_ConsumerFeePoolClaims_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "vaas/provider/v1/query.proto", @@ -2982,46 +3331,251 @@ func (m *QueryConsumerGenesisTimeResponse) MarshalToSizedBuffer(dAtA []byte) (in return len(dAtA) - i, nil } -func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { - offset -= sovQuery(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *QueryConsumerFeePoolClaimRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - dAtA[offset] = uint8(v) - return base + return dAtA[:n], nil } -func (m *QueryConsumerGenesisRequest) Size() (n int) { - if m == nil { - return 0 - } + +func (m *QueryConsumerFeePoolClaimRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerFeePoolClaimRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l + if len(m.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Depositor))) + i-- + dAtA[i] = 0x12 + } if m.ConsumerId != 0 { - n += 1 + sovQuery(uint64(m.ConsumerId)) + i = encodeVarintQuery(dAtA, i, uint64(m.ConsumerId)) + i-- + dAtA[i] = 0x8 } - return n + return len(dAtA) - i, nil } -func (m *QueryConsumerGenesisResponse) Size() (n int) { - if m == nil { - return 0 +func (m *QueryConsumerFeePoolClaimResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - var l int - _ = l - l = m.GenesisState.Size() - n += 1 + l + sovQuery(uint64(l)) - return n + return dAtA[:n], nil } -func (m *QueryConsumerChainsRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l +func (m *QueryConsumerFeePoolClaimResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerFeePoolClaimResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Claim) > 0 { + for iNdEx := len(m.Claim) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Claim[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 (m *DepositorClaim) 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 *DepositorClaim) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DepositorClaim) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Claim) > 0 { + for iNdEx := len(m.Claim) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Claim[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Depositor))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConsumerFeePoolClaimsRequest) 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 *QueryConsumerFeePoolClaimsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerFeePoolClaimsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.ConsumerId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ConsumerId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryConsumerFeePoolClaimsResponse) 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 *QueryConsumerFeePoolClaimsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerFeePoolClaimsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Claims) > 0 { + for iNdEx := len(m.Claims) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Claims[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryConsumerGenesisRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ConsumerId != 0 { + n += 1 + sovQuery(uint64(m.ConsumerId)) + } + return n +} + +func (m *QueryConsumerGenesisResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.GenesisState.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryConsumerChainsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l if m.Phase != 0 { n += 1 + sovQuery(uint64(m.Phase)) } @@ -3401,6 +3955,91 @@ func (m *QueryConsumerGenesisTimeResponse) Size() (n int) { return n } +func (m *QueryConsumerFeePoolClaimRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ConsumerId != 0 { + n += 1 + sovQuery(uint64(m.ConsumerId)) + } + l = len(m.Depositor) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryConsumerFeePoolClaimResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Claim) > 0 { + for _, e := range m.Claim { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *DepositorClaim) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Depositor) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if len(m.Claim) > 0 { + for _, e := range m.Claim { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryConsumerFeePoolClaimsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ConsumerId != 0 { + n += 1 + sovQuery(uint64(m.ConsumerId)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryConsumerFeePoolClaimsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Claims) > 0 { + for _, e := range m.Claims { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -6144,6 +6783,532 @@ func (m *QueryConsumerGenesisTimeResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryConsumerFeePoolClaimRequest) 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: QueryConsumerFeePoolClaimRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerFeePoolClaimRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerId", wireType) + } + m.ConsumerId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ConsumerId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depositor", 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.Depositor = 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 *QueryConsumerFeePoolClaimResponse) 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: QueryConsumerFeePoolClaimResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerFeePoolClaimResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Claim", 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.Claim = append(m.Claim, types2.Coin{}) + if err := m.Claim[len(m.Claim)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DepositorClaim) 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: DepositorClaim: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DepositorClaim: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depositor", 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.Depositor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Claim", 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.Claim = append(m.Claim, types2.Coin{}) + if err := m.Claim[len(m.Claim)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConsumerFeePoolClaimsRequest) 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: QueryConsumerFeePoolClaimsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerFeePoolClaimsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerId", wireType) + } + m.ConsumerId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ConsumerId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConsumerFeePoolClaimsResponse) 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: QueryConsumerFeePoolClaimsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerFeePoolClaimsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Claims", 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.Claims = append(m.Claims, DepositorClaim{}) + if err := m.Claims[len(m.Claims)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.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/vaas/provider/types/query.pb.gw.go b/x/vaas/provider/types/query.pb.gw.go index d28b805..de5a88c 100644 --- a/x/vaas/provider/types/query.pb.gw.go +++ b/x/vaas/provider/types/query.pb.gw.go @@ -623,6 +623,154 @@ func local_request_Query_QueryConsumerGenesisTime_0(ctx context.Context, marshal } +func request_Query_ConsumerFeePoolClaim_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerFeePoolClaimRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["consumer_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "consumer_id") + } + + protoReq.ConsumerId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "consumer_id", err) + } + + val, ok = pathParams["depositor"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "depositor") + } + + protoReq.Depositor, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "depositor", err) + } + + msg, err := client.ConsumerFeePoolClaim(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConsumerFeePoolClaim_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerFeePoolClaimRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["consumer_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "consumer_id") + } + + protoReq.ConsumerId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "consumer_id", err) + } + + val, ok = pathParams["depositor"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "depositor") + } + + protoReq.Depositor, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "depositor", err) + } + + msg, err := server.ConsumerFeePoolClaim(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ConsumerFeePoolClaims_0 = &utilities.DoubleArray{Encoding: map[string]int{"consumer_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_ConsumerFeePoolClaims_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerFeePoolClaimsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["consumer_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "consumer_id") + } + + protoReq.ConsumerId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "consumer_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ConsumerFeePoolClaims_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ConsumerFeePoolClaims(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConsumerFeePoolClaims_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerFeePoolClaimsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["consumer_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "consumer_id") + } + + protoReq.ConsumerId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "consumer_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ConsumerFeePoolClaims_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ConsumerFeePoolClaims(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. @@ -882,6 +1030,52 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_ConsumerFeePoolClaim_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_ConsumerFeePoolClaim_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_ConsumerFeePoolClaim_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConsumerFeePoolClaims_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_ConsumerFeePoolClaims_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_ConsumerFeePoolClaims_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1143,6 +1337,46 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_ConsumerFeePoolClaim_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_ConsumerFeePoolClaim_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_ConsumerFeePoolClaim_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConsumerFeePoolClaims_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_ConsumerFeePoolClaims_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_ConsumerFeePoolClaims_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1168,6 +1402,10 @@ var ( pattern_Query_QueryConsumerChain_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"vaas", "provider", "consumer_chain", "consumer_id"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryConsumerGenesisTime_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"vaas", "provider", "consumer_genesis_time", "consumer_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ConsumerFeePoolClaim_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"vaas", "provider", "v1", "consumer_fee_pool_claim", "consumer_id", "depositor"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ConsumerFeePoolClaims_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"vaas", "provider", "v1", "consumer_fee_pool_claims", "consumer_id"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -1192,4 +1430,8 @@ var ( forward_Query_QueryConsumerChain_0 = runtime.ForwardResponseMessage forward_Query_QueryConsumerGenesisTime_0 = runtime.ForwardResponseMessage + + forward_Query_ConsumerFeePoolClaim_0 = runtime.ForwardResponseMessage + + forward_Query_ConsumerFeePoolClaims_0 = runtime.ForwardResponseMessage ) diff --git a/x/vaas/provider/types/tx.pb.go b/x/vaas/provider/types/tx.pb.go index 812b224..c4cbdc3 100644 --- a/x/vaas/provider/types/tx.pb.go +++ b/x/vaas/provider/types/tx.pb.go @@ -9,6 +9,8 @@ import ( types "github.com/cometbft/cometbft/proto/tendermint/types" _ "github.com/cosmos/cosmos-proto" _ "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types1 "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/cosmos-sdk/types/msgservice" _ "github.com/cosmos/cosmos-sdk/types/tx/amino" _ "github.com/cosmos/gogoproto/gogoproto" @@ -730,6 +732,260 @@ func (m *MsgUpdateConsumerResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUpdateConsumerResponse proto.InternalMessageInfo +// MsgFundConsumerFeePool deposits a single-denom amount into a consumer's +// fee pool and credits the signer with shares. If the signer is the gov +// module authority, funds are pulled from the community pool and the +// distribution module account is credited as the depositor. +type MsgFundConsumerFeePool struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + ConsumerId uint64 `protobuf:"varint,2,opt,name=consumer_id,json=consumerId,proto3" json:"consumer_id,omitempty"` + Amount types1.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"` +} + +func (m *MsgFundConsumerFeePool) Reset() { *m = MsgFundConsumerFeePool{} } +func (m *MsgFundConsumerFeePool) String() string { return proto.CompactTextString(m) } +func (*MsgFundConsumerFeePool) ProtoMessage() {} +func (*MsgFundConsumerFeePool) Descriptor() ([]byte, []int) { + return fileDescriptor_a07778b1d094765c, []int{14} +} +func (m *MsgFundConsumerFeePool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFundConsumerFeePool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFundConsumerFeePool.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 *MsgFundConsumerFeePool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFundConsumerFeePool.Merge(m, src) +} +func (m *MsgFundConsumerFeePool) XXX_Size() int { + return m.Size() +} +func (m *MsgFundConsumerFeePool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFundConsumerFeePool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFundConsumerFeePool proto.InternalMessageInfo + +type MsgFundConsumerFeePoolResponse struct { +} + +func (m *MsgFundConsumerFeePoolResponse) Reset() { *m = MsgFundConsumerFeePoolResponse{} } +func (m *MsgFundConsumerFeePoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgFundConsumerFeePoolResponse) ProtoMessage() {} +func (*MsgFundConsumerFeePoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a07778b1d094765c, []int{15} +} +func (m *MsgFundConsumerFeePoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFundConsumerFeePoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFundConsumerFeePoolResponse.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 *MsgFundConsumerFeePoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFundConsumerFeePoolResponse.Merge(m, src) +} +func (m *MsgFundConsumerFeePoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgFundConsumerFeePoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFundConsumerFeePoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFundConsumerFeePoolResponse proto.InternalMessageInfo + +// MsgWithdrawConsumerFeePool withdraws tokens from the signer's share in a +// consumer fee pool across one or more denoms. Each amount is interpreted as +// "up to this much": if the signer's claim for a denom is less than the +// requested amount the handler delivers the full claim and burns all of the +// signer's shares for that denom. The transaction is atomic: if the signer +// has no shares at all for any denom in `amount` (or the pool has zero +// balance for that denom), the whole tx aborts. +// If the signer is the gov module authority, the withdrawal targets the +// distribution module account's shares and tokens are routed back to the +// community pool. +type MsgWithdrawConsumerFeePool struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + ConsumerId uint64 `protobuf:"varint,2,opt,name=consumer_id,json=consumerId,proto3" json:"consumer_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"` +} + +func (m *MsgWithdrawConsumerFeePool) Reset() { *m = MsgWithdrawConsumerFeePool{} } +func (m *MsgWithdrawConsumerFeePool) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawConsumerFeePool) ProtoMessage() {} +func (*MsgWithdrawConsumerFeePool) Descriptor() ([]byte, []int) { + return fileDescriptor_a07778b1d094765c, []int{16} +} +func (m *MsgWithdrawConsumerFeePool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawConsumerFeePool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawConsumerFeePool.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 *MsgWithdrawConsumerFeePool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawConsumerFeePool.Merge(m, src) +} +func (m *MsgWithdrawConsumerFeePool) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawConsumerFeePool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawConsumerFeePool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawConsumerFeePool proto.InternalMessageInfo + +type MsgWithdrawConsumerFeePoolResponse struct { + // total tokens actually delivered (may be less than requested due to + // truncation in the partial-withdraw branch) + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *MsgWithdrawConsumerFeePoolResponse) Reset() { *m = MsgWithdrawConsumerFeePoolResponse{} } +func (m *MsgWithdrawConsumerFeePoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawConsumerFeePoolResponse) ProtoMessage() {} +func (*MsgWithdrawConsumerFeePoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a07778b1d094765c, []int{17} +} +func (m *MsgWithdrawConsumerFeePoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawConsumerFeePoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawConsumerFeePoolResponse.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 *MsgWithdrawConsumerFeePoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawConsumerFeePoolResponse.Merge(m, src) +} +func (m *MsgWithdrawConsumerFeePoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawConsumerFeePoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawConsumerFeePoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawConsumerFeePoolResponse proto.InternalMessageInfo + +func (m *MsgWithdrawConsumerFeePoolResponse) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +// MsgSweepConsumerFeePool distributes a consumer fee pool's balance pro-rata +// to all share-holders across the specified denoms (or all denoms if `denoms` +// is empty). Truncation residue per denom is forwarded to the community pool. +// Only the consumer owner may sign. +type MsgSweepConsumerFeePool struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + ConsumerId uint64 `protobuf:"varint,2,opt,name=consumer_id,json=consumerId,proto3" json:"consumer_id,omitempty"` + // empty = all denoms with shares or balance for this consumer + Denoms []string `protobuf:"bytes,3,rep,name=denoms,proto3" json:"denoms,omitempty"` +} + +func (m *MsgSweepConsumerFeePool) Reset() { *m = MsgSweepConsumerFeePool{} } +func (m *MsgSweepConsumerFeePool) String() string { return proto.CompactTextString(m) } +func (*MsgSweepConsumerFeePool) ProtoMessage() {} +func (*MsgSweepConsumerFeePool) Descriptor() ([]byte, []int) { + return fileDescriptor_a07778b1d094765c, []int{18} +} +func (m *MsgSweepConsumerFeePool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSweepConsumerFeePool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSweepConsumerFeePool.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 *MsgSweepConsumerFeePool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSweepConsumerFeePool.Merge(m, src) +} +func (m *MsgSweepConsumerFeePool) XXX_Size() int { + return m.Size() +} +func (m *MsgSweepConsumerFeePool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSweepConsumerFeePool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSweepConsumerFeePool proto.InternalMessageInfo + +type MsgSweepConsumerFeePoolResponse struct { +} + +func (m *MsgSweepConsumerFeePoolResponse) Reset() { *m = MsgSweepConsumerFeePoolResponse{} } +func (m *MsgSweepConsumerFeePoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSweepConsumerFeePoolResponse) ProtoMessage() {} +func (*MsgSweepConsumerFeePoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a07778b1d094765c, []int{19} +} +func (m *MsgSweepConsumerFeePoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSweepConsumerFeePoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSweepConsumerFeePoolResponse.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 *MsgSweepConsumerFeePoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSweepConsumerFeePoolResponse.Merge(m, src) +} +func (m *MsgSweepConsumerFeePoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSweepConsumerFeePoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSweepConsumerFeePoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSweepConsumerFeePoolResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgAssignConsumerKey)(nil), "vaas.provider.v1.MsgAssignConsumerKey") proto.RegisterType((*MsgAssignConsumerKeyResponse)(nil), "vaas.provider.v1.MsgAssignConsumerKeyResponse") @@ -745,81 +1001,103 @@ func init() { proto.RegisterType((*MsgCreateConsumerResponse)(nil), "vaas.provider.v1.MsgCreateConsumerResponse") proto.RegisterType((*MsgUpdateConsumer)(nil), "vaas.provider.v1.MsgUpdateConsumer") proto.RegisterType((*MsgUpdateConsumerResponse)(nil), "vaas.provider.v1.MsgUpdateConsumerResponse") + proto.RegisterType((*MsgFundConsumerFeePool)(nil), "vaas.provider.v1.MsgFundConsumerFeePool") + proto.RegisterType((*MsgFundConsumerFeePoolResponse)(nil), "vaas.provider.v1.MsgFundConsumerFeePoolResponse") + proto.RegisterType((*MsgWithdrawConsumerFeePool)(nil), "vaas.provider.v1.MsgWithdrawConsumerFeePool") + proto.RegisterType((*MsgWithdrawConsumerFeePoolResponse)(nil), "vaas.provider.v1.MsgWithdrawConsumerFeePoolResponse") + proto.RegisterType((*MsgSweepConsumerFeePool)(nil), "vaas.provider.v1.MsgSweepConsumerFeePool") + proto.RegisterType((*MsgSweepConsumerFeePoolResponse)(nil), "vaas.provider.v1.MsgSweepConsumerFeePoolResponse") } func init() { proto.RegisterFile("vaas/provider/v1/tx.proto", fileDescriptor_a07778b1d094765c) } var fileDescriptor_a07778b1d094765c = []byte{ - // 1104 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4d, 0x6f, 0xdc, 0x44, - 0x18, 0x5e, 0xe7, 0xab, 0xed, 0x24, 0xa4, 0xc4, 0xa4, 0x64, 0x77, 0x29, 0xbb, 0xa9, 0x11, 0xb4, - 0x14, 0xb0, 0x49, 0x90, 0x5a, 0x29, 0x42, 0x48, 0xf9, 0x40, 0x62, 0x85, 0x56, 0x44, 0xae, 0xe8, - 0x01, 0x10, 0xd6, 0xd8, 0x9e, 0x7a, 0x47, 0xb1, 0x67, 0x2c, 0xcf, 0xec, 0xa6, 0xcb, 0x09, 0x71, - 0x40, 0x88, 0x13, 0x1c, 0xb8, 0xf7, 0x27, 0xf4, 0xc0, 0x8f, 0xe8, 0xb1, 0x70, 0x42, 0x1c, 0x2a, - 0x94, 0x48, 0x94, 0x33, 0xbf, 0x00, 0x79, 0x3c, 0xf6, 0xda, 0x5e, 0xef, 0x07, 0x85, 0x5e, 0x56, - 0x9e, 0xf7, 0x7d, 0xde, 0x8f, 0x79, 0xde, 0x67, 0x3c, 0x5e, 0xd0, 0x18, 0x40, 0xc8, 0x8c, 0x30, - 0xa2, 0x03, 0xec, 0xa2, 0xc8, 0x18, 0xec, 0x18, 0xfc, 0xbe, 0x1e, 0x46, 0x94, 0x53, 0xf5, 0xc5, - 0xd8, 0xa5, 0xa7, 0x2e, 0x7d, 0xb0, 0xd3, 0xdc, 0x80, 0x01, 0x26, 0xd4, 0x10, 0xbf, 0x09, 0xa8, - 0xb9, 0xe5, 0x50, 0x16, 0x50, 0x66, 0x04, 0xcc, 0x8b, 0x83, 0x03, 0xe6, 0x49, 0x47, 0x23, 0x71, - 0x58, 0x62, 0x65, 0x24, 0x0b, 0xe9, 0xda, 0xf4, 0xa8, 0x47, 0x13, 0x7b, 0xfc, 0x24, 0xad, 0x57, - 0x3d, 0x4a, 0x3d, 0x1f, 0x19, 0x30, 0xc4, 0x06, 0x24, 0x84, 0x72, 0xc8, 0x31, 0x25, 0x69, 0x4c, - 0x43, 0x7a, 0xc5, 0xca, 0xee, 0xdf, 0x33, 0x20, 0x19, 0x4a, 0x57, 0xab, 0xec, 0x72, 0xfb, 0x91, - 0x88, 0x95, 0xfe, 0x76, 0xd9, 0xcf, 0x71, 0x80, 0x18, 0x87, 0x41, 0x98, 0x02, 0xb0, 0xed, 0x18, - 0x0e, 0x8d, 0x90, 0xe1, 0xf8, 0x18, 0x11, 0x1e, 0x6f, 0x24, 0x79, 0x92, 0x00, 0x23, 0x06, 0xf8, - 0xd8, 0xeb, 0xf1, 0xc4, 0xcc, 0x0c, 0x8e, 0x88, 0x8b, 0xa2, 0x00, 0x27, 0xe0, 0xd1, 0x2a, 0xcd, - 0x98, 0xf3, 0xf3, 0x61, 0x88, 0x98, 0x81, 0x62, 0x12, 0x89, 0x83, 0x52, 0xc0, 0x18, 0xed, 0x19, - 0xcf, 0x02, 0xa0, 0xfd, 0xae, 0x80, 0xcd, 0x2e, 0xf3, 0xf6, 0x19, 0xc3, 0x1e, 0x39, 0xa4, 0x84, - 0xf5, 0x03, 0x14, 0x7d, 0x8c, 0x86, 0x6a, 0x1b, 0xac, 0x3a, 0x72, 0x69, 0x61, 0xb7, 0xae, 0x6c, - 0x2b, 0x37, 0x96, 0x4c, 0x90, 0x9a, 0x3a, 0xae, 0x7a, 0x1b, 0xbc, 0x90, 0xe6, 0xb2, 0xa0, 0xeb, - 0x46, 0xf5, 0x85, 0x6d, 0xe5, 0xc6, 0xa5, 0x03, 0xf5, 0xef, 0x27, 0xed, 0xf5, 0x21, 0x0c, 0xfc, - 0x3d, 0x2d, 0xb6, 0x22, 0xc6, 0x34, 0x73, 0x2d, 0x05, 0xee, 0xbb, 0x6e, 0xa4, 0x5e, 0x03, 0x6b, - 0x59, 0xe6, 0x13, 0x34, 0xac, 0x2f, 0xc6, 0x71, 0x66, 0x56, 0x2d, 0x2e, 0xfe, 0x2e, 0x58, 0x89, - 0xfb, 0x41, 0x51, 0x7d, 0x49, 0x24, 0xad, 0xff, 0xfa, 0xf3, 0x3b, 0x9b, 0x72, 0xb6, 0xfb, 0x49, - 0xd6, 0x3b, 0x3c, 0xc2, 0xc4, 0x33, 0x25, 0x6e, 0xef, 0xa5, 0xef, 0x1e, 0xb4, 0x6b, 0x7f, 0x3d, - 0x68, 0xd7, 0xbe, 0x79, 0xfa, 0xf0, 0xa6, 0x34, 0x6a, 0x2d, 0x70, 0xb5, 0x6a, 0x6f, 0x26, 0x62, - 0x21, 0x25, 0x0c, 0x69, 0x67, 0x0a, 0x78, 0xb5, 0xcb, 0xbc, 0x3b, 0x7d, 0x3b, 0xc0, 0x3c, 0x05, - 0x74, 0x31, 0xb3, 0x51, 0x0f, 0x0e, 0x30, 0xed, 0x47, 0xea, 0x2d, 0x70, 0x89, 0x09, 0x2f, 0x47, - 0x91, 0xe0, 0x60, 0x5a, 0x2f, 0x23, 0xa8, 0x7a, 0x0c, 0xd6, 0x82, 0x5c, 0x1e, 0xc1, 0xcd, 0xea, - 0xee, 0xdb, 0x3a, 0xb6, 0x1d, 0x3d, 0x3f, 0x60, 0x3d, 0x37, 0xd2, 0xc1, 0x8e, 0x9e, 0xaf, 0x6d, - 0x16, 0x32, 0x94, 0xe7, 0xb1, 0x58, 0x9e, 0xc7, 0xde, 0xcb, 0x79, 0x06, 0x46, 0xad, 0x68, 0xd7, - 0xc1, 0xeb, 0x53, 0xf7, 0x98, 0xb1, 0xf1, 0xcb, 0x42, 0x05, 0x1b, 0x47, 0xb4, 0x6f, 0xfb, 0xe8, - 0x2e, 0xe5, 0x98, 0x78, 0xcf, 0xcc, 0x86, 0x05, 0xb6, 0xdc, 0x7e, 0xe8, 0x63, 0x07, 0x72, 0x64, - 0x0d, 0x28, 0x47, 0x56, 0x2a, 0x53, 0x49, 0xcc, 0xf5, 0x3c, 0x0f, 0x42, 0xc8, 0xfa, 0x51, 0x1a, - 0x70, 0x97, 0x72, 0xf4, 0xa1, 0x84, 0x9b, 0x57, 0xdc, 0x2a, 0xb3, 0xfa, 0x25, 0xd8, 0xc2, 0xe4, - 0x5e, 0x04, 0x9d, 0xf8, 0x38, 0x5a, 0xb6, 0x4f, 0x9d, 0x13, 0xab, 0x87, 0xa0, 0x8b, 0x22, 0x41, - 0xd4, 0xea, 0xee, 0x1b, 0xb3, 0x98, 0xff, 0x48, 0xa0, 0xcd, 0x2b, 0xa3, 0x34, 0x07, 0x71, 0x96, - 0xc4, 0x5c, 0x26, 0x7f, 0xe9, 0x3f, 0x91, 0x9f, 0xa7, 0x34, 0x23, 0xff, 0x47, 0x05, 0x5c, 0xee, - 0x32, 0xef, 0xd3, 0xd0, 0x85, 0x1c, 0x1d, 0xc3, 0x08, 0x06, 0x2c, 0xa6, 0x1b, 0xf6, 0x79, 0x8f, - 0x46, 0x98, 0x0f, 0x67, 0xd3, 0x9d, 0x41, 0xd5, 0x5b, 0x60, 0x25, 0x14, 0x19, 0x24, 0xbb, 0x75, - 0xbd, 0xfc, 0x86, 0xd5, 0x93, 0x0a, 0x07, 0x4b, 0x8f, 0x9e, 0xb4, 0x6b, 0xa6, 0x44, 0xef, 0xad, - 0x8b, 0xe6, 0xb3, 0x3c, 0x5a, 0x03, 0x6c, 0x95, 0x5a, 0xca, 0xda, 0x0d, 0xc1, 0x46, 0x97, 0x79, - 0x26, 0x0a, 0xe8, 0x00, 0xa5, 0xfb, 0x9a, 0xfd, 0xca, 0xd0, 0xc1, 0x32, 0x3d, 0x8d, 0x4f, 0xf5, - 0xc2, 0x8c, 0xcd, 0x24, 0xb0, 0x3d, 0x10, 0x37, 0x94, 0x3c, 0x6b, 0xaf, 0x80, 0xc6, 0x58, 0xc5, - 0xac, 0x9d, 0xef, 0x17, 0x45, 0x3f, 0x87, 0x11, 0x82, 0x7c, 0xd4, 0xcf, 0xb3, 0xca, 0xb5, 0x01, - 0x2e, 0x3a, 0x3d, 0x88, 0x49, 0xbc, 0x09, 0xd1, 0xa9, 0x79, 0x41, 0xac, 0x3b, 0xae, 0x7a, 0x04, - 0x2e, 0x06, 0x88, 0x43, 0x17, 0x72, 0x28, 0x95, 0xa5, 0x8d, 0x93, 0x9b, 0x9d, 0x32, 0x89, 0x94, - 0x34, 0x67, 0x91, 0x2a, 0x05, 0x0d, 0x4c, 0x30, 0xc7, 0xd0, 0xc7, 0x5f, 0x89, 0x1b, 0xc4, 0x12, - 0x13, 0x40, 0x1c, 0x45, 0x4c, 0x88, 0x6b, 0x75, 0x77, 0x77, 0x72, 0xda, 0x4e, 0x21, 0xf4, 0x38, - 0x8b, 0x34, 0xeb, 0x78, 0x82, 0x47, 0xfd, 0x1c, 0xe4, 0x84, 0x9d, 0x2f, 0xb6, 0x2c, 0x4f, 0xc7, - 0x58, 0xb1, 0x4e, 0x06, 0xcf, 0x15, 0xd8, 0xc4, 0x15, 0x56, 0x29, 0x9b, 0x91, 0xe6, 0xdf, 0x17, - 0x93, 0x2a, 0xce, 0x22, 0x9d, 0xd4, 0x4c, 0x8d, 0x68, 0x7f, 0x26, 0xa3, 0x4c, 0x54, 0x97, 0x8d, - 0x32, 0x53, 0x8e, 0x32, 0x97, 0x72, 0xca, 0x65, 0x16, 0xc6, 0xa4, 0x78, 0x04, 0x36, 0x08, 0x3a, - 0xb5, 0x04, 0xda, 0x92, 0x17, 0x55, 0x72, 0x13, 0x4d, 0x49, 0x7e, 0x99, 0xa0, 0xd3, 0x4f, 0xe2, - 0x08, 0x69, 0x56, 0x3f, 0xc8, 0xc9, 0x61, 0x69, 0x5e, 0x39, 0xcc, 0x2b, 0x84, 0xe5, 0xe7, 0x20, - 0x84, 0x6d, 0xb0, 0x16, 0x6f, 0x3b, 0x93, 0xf7, 0x8a, 0x90, 0x37, 0x20, 0xe8, 0xf4, 0x50, 0x2a, - 0x7c, 0xa2, 0x54, 0x2e, 0xfc, 0x0f, 0x52, 0x19, 0x3f, 0xd0, 0xc5, 0x39, 0xa7, 0x32, 0xd9, 0xfd, - 0x69, 0x05, 0x2c, 0x76, 0x99, 0xa7, 0x9e, 0x80, 0x8d, 0xf1, 0x4f, 0x93, 0x8a, 0x1e, 0xaa, 0xae, - 0xf9, 0xa6, 0x3e, 0x1f, 0x2e, 0xd3, 0xe6, 0xb7, 0x0a, 0x68, 0x4e, 0xf9, 0x16, 0x30, 0x2a, 0xd3, - 0x4d, 0x0e, 0x68, 0xde, 0xfe, 0x97, 0x01, 0x53, 0x1a, 0x29, 0x5c, 0xc3, 0xf3, 0x34, 0x92, 0x0f, - 0x98, 0xab, 0x91, 0xaa, 0x5b, 0x49, 0xb5, 0xc1, 0x7a, 0xe9, 0x9d, 0xfa, 0x5a, 0x65, 0xaa, 0x22, - 0xa8, 0xf9, 0xd6, 0x1c, 0xa0, 0x7c, 0x8d, 0xd2, 0x61, 0xaf, 0xae, 0x51, 0x04, 0x4d, 0xa8, 0x51, - 0x2d, 0xa7, 0xb8, 0x46, 0xe9, 0xae, 0xaa, 0xae, 0x51, 0x04, 0x4d, 0xa8, 0x51, 0x7d, 0x07, 0xa9, - 0x5f, 0x80, 0xb5, 0xc2, 0xed, 0x7d, 0x6d, 0x4a, 0x83, 0x09, 0xa4, 0xf9, 0xe6, 0x4c, 0x48, 0x9a, - 0xbd, 0xb9, 0xfc, 0xf5, 0xd3, 0x87, 0x37, 0x95, 0x83, 0xce, 0xa3, 0xb3, 0x96, 0xf2, 0xf8, 0xac, - 0xa5, 0xfc, 0x71, 0xd6, 0x52, 0x7e, 0x38, 0x6f, 0xd5, 0x1e, 0x9f, 0xb7, 0x6a, 0xbf, 0x9d, 0xb7, - 0x6a, 0x9f, 0x19, 0x1e, 0xe6, 0xbd, 0xbe, 0xad, 0x3b, 0x34, 0x30, 0xa0, 0xef, 0x63, 0x62, 0x63, - 0xce, 0x0c, 0xf1, 0xf9, 0x7f, 0xdf, 0x28, 0xfe, 0x0b, 0x10, 0x1f, 0x58, 0xf6, 0x8a, 0xf8, 0x03, - 0xf0, 0xde, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x54, 0xa0, 0xc5, 0xf0, 0x9a, 0x0d, 0x00, 0x00, + // 1346 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4d, 0x6c, 0x1b, 0x45, + 0x14, 0xce, 0xe6, 0xaf, 0xcd, 0x24, 0xb4, 0x64, 0x9b, 0x36, 0xf6, 0x52, 0xec, 0x74, 0x11, 0x34, + 0x94, 0x76, 0xb7, 0x09, 0xd0, 0x4a, 0x51, 0x85, 0xd4, 0x24, 0x54, 0x44, 0xc8, 0x22, 0xda, 0x8a, + 0x22, 0x01, 0xc2, 0x9a, 0xdd, 0x9d, 0xae, 0x47, 0xf1, 0xce, 0x98, 0x9d, 0xb1, 0x5d, 0x23, 0x21, + 0x21, 0x0e, 0x08, 0x38, 0xc1, 0x91, 0x5b, 0x8f, 0x88, 0x53, 0x0f, 0x9c, 0x39, 0xf7, 0x58, 0x38, + 0x21, 0x0e, 0x05, 0xa5, 0x12, 0xe5, 0xc4, 0x81, 0x3b, 0x12, 0xda, 0xd9, 0xd9, 0xf1, 0xda, 0x5e, + 0xff, 0xb4, 0x40, 0x2f, 0x89, 0x67, 0xde, 0xf7, 0xfe, 0xbe, 0xf7, 0x66, 0xe6, 0x69, 0x41, 0xb1, + 0x05, 0x21, 0xb3, 0x1b, 0x11, 0x6d, 0x61, 0x1f, 0x45, 0x76, 0x6b, 0xc3, 0xe6, 0xb7, 0xac, 0x46, + 0x44, 0x39, 0xd5, 0x9f, 0x8e, 0x45, 0x56, 0x2a, 0xb2, 0x5a, 0x1b, 0xc6, 0x32, 0x0c, 0x31, 0xa1, + 0xb6, 0xf8, 0x9b, 0x80, 0x8c, 0x92, 0x47, 0x59, 0x48, 0x99, 0xed, 0x42, 0x86, 0xec, 0xd6, 0x86, + 0x8b, 0x38, 0xdc, 0xb0, 0x3d, 0x8a, 0x89, 0x94, 0xaf, 0x4a, 0x79, 0xc8, 0x82, 0xd8, 0x78, 0xc8, + 0x02, 0x29, 0x28, 0x26, 0x82, 0xaa, 0x58, 0xd9, 0xc9, 0x42, 0x8a, 0x56, 0x02, 0x1a, 0xd0, 0x64, + 0x3f, 0xfe, 0x25, 0x77, 0x4f, 0x07, 0x94, 0x06, 0x75, 0x64, 0xc3, 0x06, 0xb6, 0x21, 0x21, 0x94, + 0x43, 0x8e, 0x29, 0x49, 0x75, 0x8a, 0x52, 0x2a, 0x56, 0x6e, 0xf3, 0xa6, 0x0d, 0x49, 0x27, 0x0d, + 0xb1, 0x5f, 0xe4, 0x37, 0x23, 0xa1, 0x2b, 0xe5, 0xe5, 0x7e, 0x39, 0xc7, 0x21, 0x62, 0x1c, 0x86, + 0x8d, 0x14, 0x80, 0x5d, 0xcf, 0xf6, 0x68, 0x84, 0x6c, 0xaf, 0x8e, 0x11, 0xe1, 0x71, 0x22, 0xc9, + 0x2f, 0x09, 0xb0, 0x63, 0x40, 0x1d, 0x07, 0x35, 0x9e, 0x6c, 0x33, 0x9b, 0x23, 0xe2, 0xa3, 0x28, + 0xc4, 0x09, 0xb8, 0xbb, 0x4a, 0x2d, 0x66, 0xe4, 0xbc, 0xd3, 0x40, 0xcc, 0x46, 0x31, 0xc9, 0xc4, + 0x43, 0x29, 0x60, 0xa0, 0x2c, 0xaa, 0x0e, 0x02, 0x60, 0xfe, 0xa2, 0x81, 0x95, 0x0a, 0x0b, 0xae, + 0x32, 0x86, 0x03, 0xb2, 0x43, 0x09, 0x6b, 0x86, 0x28, 0x7a, 0x13, 0x75, 0xf4, 0x32, 0x58, 0xf4, + 0xe4, 0xb2, 0x8a, 0xfd, 0x82, 0xb6, 0xa6, 0xad, 0xcf, 0x3a, 0x20, 0xdd, 0xda, 0xf3, 0xf5, 0xcb, + 0xe0, 0xa9, 0xd4, 0x56, 0x15, 0xfa, 0x7e, 0x54, 0x98, 0x5e, 0xd3, 0xd6, 0x17, 0xb6, 0xf5, 0xbf, + 0xee, 0x97, 0x8f, 0x75, 0x60, 0x58, 0xdf, 0x32, 0xe3, 0x5d, 0xc4, 0x98, 0xe9, 0x2c, 0xa5, 0xc0, + 0xab, 0xbe, 0x1f, 0xe9, 0x67, 0xc0, 0x92, 0xb2, 0x7c, 0x80, 0x3a, 0x85, 0x99, 0x58, 0xcf, 0x51, + 0xde, 0x62, 0xe7, 0x17, 0xc1, 0x7c, 0x1c, 0x0f, 0x8a, 0x0a, 0xb3, 0xc2, 0x68, 0xe1, 0xa7, 0xef, + 0x2f, 0xac, 0xc8, 0xda, 0x5e, 0x4d, 0xac, 0x5e, 0xe7, 0x11, 0x26, 0x81, 0x23, 0x71, 0x5b, 0x27, + 0x3e, 0xbf, 0x5d, 0x9e, 0xfa, 0xe3, 0x76, 0x79, 0xea, 0xd3, 0x87, 0x77, 0xce, 0xc9, 0x4d, 0xb3, + 0x04, 0x4e, 0xe7, 0xe5, 0xe6, 0x20, 0xd6, 0xa0, 0x84, 0x21, 0xf3, 0x50, 0x03, 0xcf, 0x56, 0x58, + 0x70, 0xbd, 0xe9, 0x86, 0x98, 0xa7, 0x80, 0x0a, 0x66, 0x2e, 0xaa, 0xc1, 0x16, 0xa6, 0xcd, 0x48, + 0xbf, 0x04, 0x16, 0x98, 0x90, 0x72, 0x14, 0x09, 0x0e, 0x46, 0xc5, 0xd2, 0x85, 0xea, 0xfb, 0x60, + 0x29, 0xcc, 0xd8, 0x11, 0xdc, 0x2c, 0x6e, 0x9e, 0xb7, 0xb0, 0xeb, 0x59, 0xd9, 0x02, 0x5b, 0x99, + 0x92, 0xb6, 0x36, 0xac, 0xac, 0x6f, 0xa7, 0xc7, 0x42, 0x7f, 0x3d, 0x66, 0xfa, 0xeb, 0xb1, 0x75, + 0x2a, 0xcb, 0x40, 0x37, 0x14, 0xf3, 0x2c, 0x78, 0x7e, 0x64, 0x8e, 0x8a, 0x8d, 0x1f, 0xa7, 0x73, + 0xd8, 0xd8, 0xa5, 0x4d, 0xb7, 0x8e, 0x6e, 0x50, 0x8e, 0x49, 0xf0, 0xd8, 0x6c, 0x54, 0xc1, 0xaa, + 0xdf, 0x6c, 0xd4, 0xb1, 0x07, 0x39, 0xaa, 0xb6, 0x28, 0x47, 0xd5, 0xb4, 0x4d, 0x25, 0x31, 0x67, + 0xb3, 0x3c, 0x88, 0x46, 0xb6, 0x76, 0x53, 0x85, 0x1b, 0x94, 0xa3, 0xd7, 0x25, 0xdc, 0x39, 0xe9, + 0xe7, 0x6d, 0xeb, 0x1f, 0x80, 0x55, 0x4c, 0x6e, 0x46, 0xd0, 0x8b, 0x8f, 0x63, 0xd5, 0xad, 0x53, + 0xef, 0xa0, 0x5a, 0x43, 0xd0, 0x47, 0x91, 0x20, 0x6a, 0x71, 0xf3, 0x85, 0x71, 0xcc, 0xbf, 0x21, + 0xd0, 0xce, 0xc9, 0xae, 0x99, 0xed, 0xd8, 0x4a, 0xb2, 0xdd, 0x4f, 0xfe, 0xec, 0xbf, 0x22, 0x3f, + 0x4b, 0xa9, 0x22, 0xff, 0x6b, 0x0d, 0x1c, 0xaf, 0xb0, 0xe0, 0xed, 0x86, 0x0f, 0x39, 0xda, 0x87, + 0x11, 0x0c, 0x59, 0x4c, 0x37, 0x6c, 0xf2, 0x1a, 0x8d, 0x30, 0xef, 0x8c, 0xa7, 0x5b, 0x41, 0xf5, + 0x4b, 0x60, 0xbe, 0x21, 0x2c, 0x48, 0x76, 0x0b, 0x56, 0xff, 0x0d, 0x6c, 0x25, 0x1e, 0xb6, 0x67, + 0xef, 0xde, 0x2f, 0x4f, 0x39, 0x12, 0xbd, 0x75, 0x4c, 0x04, 0xaf, 0xec, 0x98, 0x45, 0xb0, 0xda, + 0x17, 0x92, 0x0a, 0xb7, 0x01, 0x96, 0x2b, 0x2c, 0x70, 0x50, 0x48, 0x5b, 0x28, 0xcd, 0x6b, 0xfc, + 0x95, 0x61, 0x81, 0x39, 0xda, 0x8e, 0x4f, 0xf5, 0xf4, 0x98, 0x64, 0x12, 0xd8, 0x16, 0x88, 0x03, + 0x4a, 0x7e, 0x9b, 0xcf, 0x80, 0xe2, 0x80, 0x47, 0x15, 0xce, 0x97, 0x33, 0x22, 0x9e, 0x9d, 0x08, + 0x41, 0xde, 0x8d, 0xe7, 0x71, 0xdb, 0xb5, 0x08, 0x8e, 0x7a, 0x35, 0x88, 0x49, 0x9c, 0x84, 0x88, + 0xd4, 0x39, 0x22, 0xd6, 0x7b, 0xbe, 0xbe, 0x0b, 0x8e, 0x86, 0x88, 0x43, 0x1f, 0x72, 0x28, 0x3b, + 0xcb, 0x1c, 0x24, 0x57, 0x9d, 0x32, 0x89, 0x94, 0x34, 0x2b, 0x4d, 0x9d, 0x82, 0x22, 0x26, 0x98, + 0x63, 0x58, 0xc7, 0x1f, 0x89, 0x17, 0xa4, 0x2a, 0x2a, 0x80, 0x38, 0x8a, 0x98, 0x68, 0xae, 0xc5, + 0xcd, 0xcd, 0xe1, 0x66, 0xf7, 0x7a, 0x54, 0xf7, 0x95, 0xa6, 0x53, 0xc0, 0x43, 0x24, 0xfa, 0x7b, + 0x20, 0xd3, 0xd8, 0x59, 0x67, 0x73, 0xf2, 0x74, 0x0c, 0x38, 0xdb, 0x53, 0xf0, 0x8c, 0x83, 0x15, + 0x9c, 0xb3, 0x2b, 0xdb, 0xa6, 0xdb, 0xf3, 0x57, 0x44, 0xa5, 0x7a, 0x6b, 0x91, 0x56, 0x6a, 0x6c, + 0x8f, 0x98, 0xbf, 0x27, 0xa5, 0x4c, 0xba, 0x4e, 0x95, 0x52, 0x75, 0x8e, 0x36, 0x51, 0xe7, 0xf4, + 0xbb, 0x99, 0x1e, 0x68, 0xc5, 0x5d, 0xb0, 0x4c, 0x50, 0xbb, 0x2a, 0xd0, 0x55, 0xf9, 0x50, 0x25, + 0x2f, 0xd1, 0x08, 0xe3, 0xc7, 0x09, 0x6a, 0xbf, 0x15, 0x6b, 0xc8, 0x6d, 0xfd, 0xb5, 0x4c, 0x3b, + 0xcc, 0x4e, 0xda, 0x0e, 0x93, 0x36, 0xc2, 0xdc, 0xff, 0xd0, 0x08, 0x6b, 0x60, 0x29, 0x4e, 0x5b, + 0xb5, 0xf7, 0xbc, 0x68, 0x6f, 0x40, 0x50, 0x7b, 0x47, 0x76, 0xf8, 0xd0, 0x56, 0x39, 0xf2, 0x1f, + 0xb4, 0xca, 0xe0, 0x81, 0xee, 0xad, 0xb3, 0x3a, 0xd0, 0x3f, 0x68, 0xe0, 0x54, 0x85, 0x05, 0xd7, + 0x9a, 0xc4, 0x4f, 0x65, 0xd7, 0x10, 0xda, 0xa7, 0xb4, 0x9e, 0x99, 0x0d, 0xb4, 0xc9, 0x66, 0x83, + 0xf1, 0xcd, 0x70, 0x05, 0xcc, 0xc3, 0x90, 0x36, 0x09, 0x97, 0x67, 0xba, 0x68, 0x49, 0x7b, 0xf1, + 0x34, 0x6a, 0xc9, 0x69, 0xd4, 0xda, 0xa1, 0x98, 0x6c, 0x2f, 0xc4, 0x47, 0xf9, 0xdb, 0x87, 0x77, + 0xce, 0x69, 0x8e, 0xd4, 0xc9, 0x1f, 0x3d, 0xd6, 0x40, 0x29, 0x3f, 0x7e, 0x95, 0xe2, 0x9f, 0x1a, + 0x30, 0x2a, 0x2c, 0x78, 0x07, 0xf3, 0x9a, 0x1f, 0xc1, 0xf6, 0x13, 0x48, 0xb3, 0x96, 0x49, 0x73, + 0x66, 0x74, 0x9a, 0xaf, 0xc6, 0x69, 0x7e, 0xf7, 0x6b, 0x79, 0x3d, 0xc0, 0xbc, 0xd6, 0x74, 0x2d, + 0x8f, 0x86, 0x72, 0xb6, 0x96, 0xff, 0x2e, 0x30, 0xff, 0x20, 0x19, 0x39, 0x85, 0x02, 0x9b, 0x80, + 0x92, 0x2f, 0x34, 0x60, 0x0e, 0x4f, 0x58, 0xdd, 0x10, 0x9e, 0x8a, 0x52, 0x1b, 0x17, 0xe5, 0xc5, + 0x47, 0x8d, 0x32, 0x0d, 0xd0, 0xfc, 0x46, 0x13, 0x6f, 0xdb, 0xf5, 0x36, 0x42, 0x8d, 0x27, 0xc0, + 0xfc, 0x29, 0x30, 0xef, 0x23, 0x42, 0x43, 0x26, 0x98, 0x5f, 0x70, 0xe4, 0x2a, 0x9f, 0xa7, 0x33, + 0xa0, 0x3c, 0x24, 0xb4, 0x94, 0xa3, 0xcd, 0xbf, 0x8f, 0x82, 0x99, 0x0a, 0x0b, 0xf4, 0x03, 0xb0, + 0x3c, 0x38, 0xb9, 0xe7, 0x1c, 0xd1, 0xbc, 0x29, 0xd8, 0xb0, 0x26, 0xc3, 0xa9, 0xc2, 0x7c, 0xa6, + 0x01, 0x63, 0xc4, 0xa8, 0x6c, 0xe7, 0x9a, 0x1b, 0xae, 0x60, 0x5c, 0x7e, 0x44, 0x85, 0x11, 0x81, + 0xf4, 0x4c, 0xa9, 0x93, 0x04, 0x92, 0x55, 0x98, 0x28, 0x90, 0xbc, 0xa1, 0x4d, 0x77, 0xc1, 0xb1, + 0xbe, 0x91, 0xe3, 0xb9, 0x5c, 0x53, 0xbd, 0x20, 0xe3, 0xa5, 0x09, 0x40, 0x59, 0x1f, 0x7d, 0x6f, + 0x61, 0xbe, 0x8f, 0x5e, 0xd0, 0x10, 0x1f, 0xf9, 0xb7, 0x6d, 0xec, 0xa3, 0x6f, 0x94, 0xcb, 0xf7, + 0xd1, 0x0b, 0x1a, 0xe2, 0x23, 0x7f, 0x44, 0xd3, 0xdf, 0x07, 0x4b, 0x3d, 0xc3, 0xed, 0x99, 0x11, + 0x01, 0x26, 0x10, 0xe3, 0xc5, 0xb1, 0x10, 0x65, 0xfd, 0x43, 0x70, 0x22, 0xef, 0xad, 0x58, 0xcf, + 0xb5, 0x90, 0x83, 0x34, 0x2e, 0x4e, 0x8a, 0x54, 0x2e, 0x3f, 0x06, 0xab, 0xc3, 0xee, 0xee, 0xf3, + 0xb9, 0xc6, 0x86, 0xa0, 0x8d, 0x57, 0x1e, 0x05, 0xad, 0xdc, 0x73, 0xb0, 0x92, 0x7b, 0x7b, 0xe5, + 0x93, 0x96, 0x07, 0x35, 0x36, 0x26, 0x86, 0xa6, 0x5e, 0x8d, 0xb9, 0x4f, 0xe2, 0x7b, 0x7e, 0x7b, + 0xef, 0xee, 0x61, 0x49, 0xbb, 0x77, 0x58, 0xd2, 0x7e, 0x3b, 0x2c, 0x69, 0x5f, 0x3d, 0x28, 0x4d, + 0xdd, 0x7b, 0x50, 0x9a, 0xfa, 0xf9, 0x41, 0x69, 0xea, 0x5d, 0x3b, 0x73, 0x15, 0xc3, 0x7a, 0x1d, + 0x13, 0x17, 0x73, 0x66, 0x8b, 0xaf, 0x10, 0xb7, 0xec, 0xde, 0x8f, 0x11, 0xe2, 0x5e, 0x76, 0xe7, + 0xc5, 0x77, 0x88, 0x97, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x54, 0x53, 0x7a, 0x78, 0x41, 0x12, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -841,6 +1119,9 @@ type MsgClient interface { UpdateConsumer(ctx context.Context, in *MsgUpdateConsumer, opts ...grpc.CallOption) (*MsgUpdateConsumerResponse, error) RemoveConsumer(ctx context.Context, in *MsgRemoveConsumer, opts ...grpc.CallOption) (*MsgRemoveConsumerResponse, error) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) + FundConsumerFeePool(ctx context.Context, in *MsgFundConsumerFeePool, opts ...grpc.CallOption) (*MsgFundConsumerFeePoolResponse, error) + WithdrawConsumerFeePool(ctx context.Context, in *MsgWithdrawConsumerFeePool, opts ...grpc.CallOption) (*MsgWithdrawConsumerFeePoolResponse, error) + SweepConsumerFeePool(ctx context.Context, in *MsgSweepConsumerFeePool, opts ...grpc.CallOption) (*MsgSweepConsumerFeePoolResponse, error) } type msgClient struct { @@ -914,6 +1195,33 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts return out, nil } +func (c *msgClient) FundConsumerFeePool(ctx context.Context, in *MsgFundConsumerFeePool, opts ...grpc.CallOption) (*MsgFundConsumerFeePoolResponse, error) { + out := new(MsgFundConsumerFeePoolResponse) + err := c.cc.Invoke(ctx, "/vaas.provider.v1.Msg/FundConsumerFeePool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) WithdrawConsumerFeePool(ctx context.Context, in *MsgWithdrawConsumerFeePool, opts ...grpc.CallOption) (*MsgWithdrawConsumerFeePoolResponse, error) { + out := new(MsgWithdrawConsumerFeePoolResponse) + err := c.cc.Invoke(ctx, "/vaas.provider.v1.Msg/WithdrawConsumerFeePool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SweepConsumerFeePool(ctx context.Context, in *MsgSweepConsumerFeePool, opts ...grpc.CallOption) (*MsgSweepConsumerFeePoolResponse, error) { + out := new(MsgSweepConsumerFeePoolResponse) + err := c.cc.Invoke(ctx, "/vaas.provider.v1.Msg/SweepConsumerFeePool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { AssignConsumerKey(context.Context, *MsgAssignConsumerKey) (*MsgAssignConsumerKeyResponse, error) @@ -923,6 +1231,9 @@ type MsgServer interface { UpdateConsumer(context.Context, *MsgUpdateConsumer) (*MsgUpdateConsumerResponse, error) RemoveConsumer(context.Context, *MsgRemoveConsumer) (*MsgRemoveConsumerResponse, error) UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) + FundConsumerFeePool(context.Context, *MsgFundConsumerFeePool) (*MsgFundConsumerFeePoolResponse, error) + WithdrawConsumerFeePool(context.Context, *MsgWithdrawConsumerFeePool) (*MsgWithdrawConsumerFeePoolResponse, error) + SweepConsumerFeePool(context.Context, *MsgSweepConsumerFeePool) (*MsgSweepConsumerFeePoolResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -950,6 +1261,15 @@ func (*UnimplementedMsgServer) RemoveConsumer(ctx context.Context, req *MsgRemov func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") } +func (*UnimplementedMsgServer) FundConsumerFeePool(ctx context.Context, req *MsgFundConsumerFeePool) (*MsgFundConsumerFeePoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FundConsumerFeePool not implemented") +} +func (*UnimplementedMsgServer) WithdrawConsumerFeePool(ctx context.Context, req *MsgWithdrawConsumerFeePool) (*MsgWithdrawConsumerFeePoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WithdrawConsumerFeePool not implemented") +} +func (*UnimplementedMsgServer) SweepConsumerFeePool(ctx context.Context, req *MsgSweepConsumerFeePool) (*MsgSweepConsumerFeePoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SweepConsumerFeePool not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1081,6 +1401,60 @@ func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Msg_FundConsumerFeePool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgFundConsumerFeePool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).FundConsumerFeePool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vaas.provider.v1.Msg/FundConsumerFeePool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).FundConsumerFeePool(ctx, req.(*MsgFundConsumerFeePool)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_WithdrawConsumerFeePool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgWithdrawConsumerFeePool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).WithdrawConsumerFeePool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vaas.provider.v1.Msg/WithdrawConsumerFeePool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).WithdrawConsumerFeePool(ctx, req.(*MsgWithdrawConsumerFeePool)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SweepConsumerFeePool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSweepConsumerFeePool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SweepConsumerFeePool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vaas.provider.v1.Msg/SweepConsumerFeePool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SweepConsumerFeePool(ctx, req.(*MsgSweepConsumerFeePool)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "vaas.provider.v1.Msg", HandlerType: (*MsgServer)(nil), @@ -1113,6 +1487,18 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "UpdateParams", Handler: _Msg_UpdateParams_Handler, }, + { + MethodName: "FundConsumerFeePool", + Handler: _Msg_FundConsumerFeePool_Handler, + }, + { + MethodName: "WithdrawConsumerFeePool", + Handler: _Msg_WithdrawConsumerFeePool_Handler, + }, + { + MethodName: "SweepConsumerFeePool", + Handler: _Msg_SweepConsumerFeePool_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "vaas/provider/v1/tx.proto", @@ -1670,39 +2056,260 @@ func (m *MsgUpdateConsumerResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } -func encodeVarintTx(dAtA []byte, offset int, v uint64) int { - offset -= sovTx(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *MsgFundConsumerFeePool) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - dAtA[offset] = uint8(v) - return base + return dAtA[:n], nil } -func (m *MsgAssignConsumerKey) Size() (n int) { - if m == nil { - return 0 - } + +func (m *MsgFundConsumerFeePool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFundConsumerFeePool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.ConsumerId != 0 { - n += 1 + sovTx(uint64(m.ConsumerId)) - } - l = len(m.ProviderAddr) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) } - l = len(m.ConsumerKey) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + i-- + dAtA[i] = 0x1a + if m.ConsumerId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.ConsumerId)) + i-- + dAtA[i] = 0x10 } - l = len(m.Signer) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil +} + +func (m *MsgFundConsumerFeePoolResponse) 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 *MsgFundConsumerFeePoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFundConsumerFeePoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgWithdrawConsumerFeePool) 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 *MsgWithdrawConsumerFeePool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawConsumerFeePool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.ConsumerId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.ConsumerId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgWithdrawConsumerFeePoolResponse) 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 *MsgWithdrawConsumerFeePoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawConsumerFeePoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *MsgSweepConsumerFeePool) 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 *MsgSweepConsumerFeePool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSweepConsumerFeePool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Denoms) > 0 { + for iNdEx := len(m.Denoms) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Denoms[iNdEx]) + copy(dAtA[i:], m.Denoms[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Denoms[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.ConsumerId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.ConsumerId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSweepConsumerFeePoolResponse) 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 *MsgSweepConsumerFeePoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSweepConsumerFeePoolResponse) 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 + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgAssignConsumerKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ConsumerId != 0 { + n += 1 + sovTx(uint64(m.ConsumerId)) + } + l = len(m.ProviderAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ConsumerKey) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n } func (m *MsgAssignConsumerKeyResponse) Size() (n int) { @@ -1909,6 +2516,101 @@ func (m *MsgUpdateConsumerResponse) Size() (n int) { return n } +func (m *MsgFundConsumerFeePool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ConsumerId != 0 { + n += 1 + sovTx(uint64(m.ConsumerId)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgFundConsumerFeePoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgWithdrawConsumerFeePool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ConsumerId != 0 { + n += 1 + sovTx(uint64(m.ConsumerId)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgWithdrawConsumerFeePoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgSweepConsumerFeePool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ConsumerId != 0 { + n += 1 + sovTx(uint64(m.ConsumerId)) + } + if len(m.Denoms) > 0 { + for _, s := range m.Denoms { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgSweepConsumerFeePoolResponse) 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 } @@ -3467,6 +4169,592 @@ func (m *MsgUpdateConsumerResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgFundConsumerFeePool) 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: MsgFundConsumerFeePool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFundConsumerFeePool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerId", wireType) + } + m.ConsumerId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ConsumerId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + 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 *MsgFundConsumerFeePoolResponse) 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: MsgFundConsumerFeePoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFundConsumerFeePoolResponse: 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 (m *MsgWithdrawConsumerFeePool) 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: MsgWithdrawConsumerFeePool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawConsumerFeePool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerId", wireType) + } + m.ConsumerId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ConsumerId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + 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 + } + m.Amount = append(m.Amount, types1.Coin{}) + if err := m.Amount[len(m.Amount)-1].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 *MsgWithdrawConsumerFeePoolResponse) 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: MsgWithdrawConsumerFeePoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawConsumerFeePoolResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + 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 + } + m.Amount = append(m.Amount, types1.Coin{}) + if err := m.Amount[len(m.Amount)-1].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 *MsgSweepConsumerFeePool) 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: MsgSweepConsumerFeePool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSweepConsumerFeePool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerId", wireType) + } + m.ConsumerId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ConsumerId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denoms", 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.Denoms = append(m.Denoms, string(dAtA[iNdEx:postIndex])) + 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 *MsgSweepConsumerFeePoolResponse) 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: MsgSweepConsumerFeePoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSweepConsumerFeePoolResponse: 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/vaas/types/expected_keepers.go b/x/vaas/types/expected_keepers.go index 9f002ba..b741f25 100644 --- a/x/vaas/types/expected_keepers.go +++ b/x/vaas/types/expected_keepers.go @@ -84,6 +84,7 @@ type ConsumerHooks interface { // BankKeeper defines the expected interface needed to retrieve account balances. type BankKeeper interface { GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin + GetAllBalances(ctx context.Context, addr sdk.AccAddress) sdk.Coins SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error @@ -105,3 +106,10 @@ type ChannelV2Keeper interface { type IBCTransferKeeper interface { Transfer(context.Context, *transfertypes.MsgTransfer) (*transfertypes.MsgTransferResponse, error) } + +// DistributionKeeper defines the expected interface needed to fund and spend +// from the cosmos-sdk x/distribution community pool. +type DistributionKeeper interface { + FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error + DistributeFromFeePool(ctx context.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) error +}