Skip to content

Commit 48a2186

Browse files
authored
feat!: provider proposal for changing reward denoms (#1280)
* new provider prop type * add methods and tests for new prop, update docs * remove old tx, fix tests * e2e handling * fix command type * boilerplate * fix e2e tests * Update CHANGELOG.md * lint * validate denoms * Update proposal.go * rm msg string * fix tests * rm chain in change denom action * lint * test for invalid denom * events for both add and remove * Update proposal_test.go
1 parent d842bca commit 48a2186

File tree

26 files changed

+872
-808
lines changed

26 files changed

+872
-808
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
Add an entry to the unreleased provider section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a provider release.
66

7+
* (feature!) [#1280](https://github.com/cosmos/interchain-security/pull/1280) provider proposal for changing reward denoms
78
* (feature!) [#1244](https://github.com/cosmos/interchain-security/pull/1244) Update the default consumer unbonding period to 2 weeks.
89
* (deps) [#1259](https://github.com/cosmos/interchain-security/pull/1259) Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to [v0.47.5](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.5).
910
* (deps!) [#1258](https://github.com/cosmos/interchain-security/pull/1258) Bump [ibc-go](https://github.com/cosmos/ibc-go) to [v7.3.0](https://github.com/cosmos/ibc-go/releases/tag/v7.3.0).

app/provider/app.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ var (
141141
ibcproviderclient.ConsumerAdditionProposalHandler,
142142
ibcproviderclient.ConsumerRemovalProposalHandler,
143143
ibcproviderclient.EquivocationProposalHandler,
144+
ibcproviderclient.ChangeRewardDenomsProposalHandler,
144145
},
145146
),
146147
params.AppModuleBasic{},

docs/docs/features/proposals.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,23 @@ Minimal example:
105105
}
106106
```
107107

108+
## ChangeRewardDenomProposal
109+
:::tip
110+
`ChangeRewardDenomProposal` will only be accepted on the provider chain if at least one of the denomsToAdd or denomsToRemove fields is populated with at least one denom. Also, a denom cannot be repeated in both sets.
111+
:::
112+
113+
Proposal type used to mutate the set of denoms accepted by the provider as rewards.
114+
115+
Minimal example:
116+
```js
117+
{
118+
"title": "Add untrn as a reward denom",
119+
"description": "Here is more information about the proposal",
120+
"denomsToAdd": ["untrn"],
121+
"denomsToRemove": []
122+
}
123+
```
124+
108125
### Notes
109126
When submitting equivocation evidence through an `EquivocationProposal` please take note that you need to use the consensus address (`valcons`) of the offending validator on the **provider chain**.
110127
Besides that, the `height` and the `time` fields should be mapped to the **provider chain** to avoid your evidence being rejected.

proto/interchain_security/ccv/provider/v1/provider.proto

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,19 @@ message EquivocationProposal {
116116
repeated cosmos.evidence.v1beta1.Equivocation equivocations = 3;
117117
}
118118

119+
// ChangeRewardDenomsProposal is a governance proposal on the provider chain to
120+
// mutate the set of denoms accepted by the provider as rewards.
121+
message ChangeRewardDenomsProposal {
122+
// the title of the proposal
123+
string title = 1;
124+
// the description of the proposal
125+
string description = 2;
126+
// the list of consumer reward denoms to add
127+
repeated string denoms_to_add = 3;
128+
// the list of consumer reward denoms to remove
129+
repeated string denoms_to_remove = 4;
130+
}
131+
119132
// A persisted queue entry indicating that a slash packet data instance needs to
120133
// be handled. This type belongs in the "global" queue, to coordinate slash
121134
// packet handling times between consumers.

proto/interchain_security/ccv/provider/v1/tx.proto

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import "google/protobuf/any.proto";
1212
service Msg {
1313
rpc AssignConsumerKey(MsgAssignConsumerKey)
1414
returns (MsgAssignConsumerKeyResponse);
15-
rpc RegisterConsumerRewardDenom(MsgRegisterConsumerRewardDenom)
16-
returns (MsgRegisterConsumerRewardDenomResponse);
1715
}
1816

1917
message MsgAssignConsumerKey {
@@ -30,18 +28,3 @@ message MsgAssignConsumerKey {
3028
}
3129

3230
message MsgAssignConsumerKeyResponse {}
33-
34-
// MsgRegisterConsumerRewardDenom allows an account to register
35-
// a consumer reward denom, i.e., add it to the list of denoms
36-
// accepted by the provider as rewards.
37-
message MsgRegisterConsumerRewardDenom {
38-
option (gogoproto.equal) = false;
39-
option (gogoproto.goproto_getters) = false;
40-
41-
string denom = 1;
42-
string depositor = 2;
43-
}
44-
45-
// MsgRegisterConsumerRewardDenomResponse defines the
46-
// Msg/RegisterConsumerRewardDenom response type.
47-
message MsgRegisterConsumerRewardDenomResponse {}

tests/e2e/actions.go

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,35 +1751,63 @@ func (tr TestRun) registerRepresentative(
17511751
wg.Wait()
17521752
}
17531753

1754-
type registerConsumerRewardDenomAction struct {
1755-
chain chainID
1756-
from validatorID
1757-
denom string
1754+
type submitChangeRewardDenomsProposalAction struct {
1755+
denom string
1756+
deposit uint
1757+
from validatorID
17581758
}
17591759

1760-
func (tr TestRun) registerConsumerRewardDenom(action registerConsumerRewardDenomAction, verbose bool) {
1760+
func (tr TestRun) submitChangeRewardDenomsProposal(action submitChangeRewardDenomsProposalAction, verbose bool) {
1761+
providerChain := tr.chainConfigs[chainID("provi")]
1762+
1763+
prop := client.ChangeRewardDenomsProposalJSON{
1764+
Summary: "Change reward denoms",
1765+
ChangeRewardDenomsProposal: types.ChangeRewardDenomsProposal{
1766+
Title: "Change reward denoms",
1767+
Description: "Change reward denoms",
1768+
DenomsToAdd: []string{action.denom},
1769+
DenomsToRemove: []string{"stake"},
1770+
},
1771+
Deposit: fmt.Sprint(action.deposit) + `stake`,
1772+
}
1773+
1774+
bz, err := json.Marshal(prop)
1775+
if err != nil {
1776+
log.Fatal(err)
1777+
}
1778+
1779+
jsonStr := string(bz)
1780+
if strings.Contains(jsonStr, "'") {
1781+
log.Fatal("prop json contains single quote")
1782+
}
1783+
17611784
//#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments.
1762-
bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[action.chain].binaryName,
1763-
"tx", "provider", "register-consumer-reward-denom", action.denom,
1785+
bz, err = exec.Command("docker", "exec", tr.containerConfig.instanceName,
1786+
"/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, "/change-reward-denoms-proposal.json")).CombinedOutput()
17641787

1788+
if err != nil {
1789+
log.Fatal(err, "\n", string(bz))
1790+
}
1791+
1792+
//#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments.
1793+
// CHANGE REWARDS DENOM PROPOSAL
1794+
bz, err = exec.Command("docker", "exec", tr.containerConfig.instanceName, providerChain.binaryName,
1795+
"tx", "gov", "submit-legacy-proposal", "change-reward-denoms", "/change-reward-denoms-proposal.json",
17651796
`--from`, `validator`+fmt.Sprint(action.from),
1766-
`--chain-id`, string(action.chain),
1767-
`--home`, tr.getValidatorHome(action.chain, action.from),
1768-
`--node`, tr.getValidatorNode(action.chain, action.from),
1797+
`--chain-id`, string(providerChain.chainId),
1798+
`--home`, tr.getValidatorHome(providerChain.chainId, action.from),
1799+
`--node`, tr.getValidatorNode(providerChain.chainId, action.from),
17691800
`--gas`, "9000000",
17701801
`--keyring-backend`, `test`,
17711802
`-y`,
17721803
).CombinedOutput()
17731804

1774-
if verbose {
1775-
fmt.Println("redelegate cmd:", string(bz))
1776-
}
1777-
17781805
if err != nil {
17791806
log.Fatal(err, "\n", string(bz))
17801807
}
17811808

1782-
tr.waitBlocks(action.chain, 2, 10*time.Second)
1809+
// wait for inclusion in a block -> '--broadcast-mode block' is deprecated
1810+
tr.waitBlocks(chainID("provi"), 2, 30*time.Second)
17831811
}
17841812

17851813
// Creates an additional node on selected chain

tests/e2e/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,8 @@ func (tr *TestRun) runStep(step Step, verbose bool) {
258258
tr.waitForSlashThrottleDequeue(action, verbose)
259259
case startRelayerAction:
260260
tr.startRelayer(action, verbose)
261-
case registerConsumerRewardDenomAction:
262-
tr.registerConsumerRewardDenom(action, verbose)
261+
case submitChangeRewardDenomsProposalAction:
262+
tr.submitChangeRewardDenomsProposal(action, verbose)
263263
default:
264264
log.Fatalf("unknown action in testRun %s: %#v", tr.name, action)
265265
}

tests/e2e/steps_democracy.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,19 +133,29 @@ func stepsDemocracy(consumerName string) []Step {
133133
},
134134
},
135135
{
136-
action: registerConsumerRewardDenomAction{
137-
chain: chainID("provi"),
138-
from: validatorID("bob"),
139-
denom: consumerRewardDenom,
136+
action: submitChangeRewardDenomsProposalAction{
137+
denom: consumerRewardDenom,
138+
deposit: 10000001,
139+
from: validatorID("bob"),
140+
},
141+
state: State{
142+
chainID("provi"): ChainState{
143+
// Denom not yet registered, gov prop needs to pass first
144+
RegisteredConsumerRewardDenoms: &[]string{},
145+
},
146+
},
147+
},
148+
{
149+
action: voteGovProposalAction{
150+
chain: chainID("provi"),
151+
from: []validatorID{validatorID("alice"), validatorID("bob"), validatorID("carol")},
152+
vote: []string{"yes", "yes", "yes"},
153+
propNumber: 2,
140154
},
141155
state: State{
142156
chainID("provi"): ChainState{
143157
// Check that the denom is registered on provider chain
144158
RegisteredConsumerRewardDenoms: &[]string{consumerRewardDenom},
145-
ValBalances: &map[validatorID]uint{
146-
// make sure that bob's account was debited
147-
validatorID("bob"): 9490000000,
148-
},
149159
},
150160
},
151161
},

tests/e2e/steps_reward_denom.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,29 @@ func stepsRewardDenomConsumer(consumerName string) []Step {
131131
},
132132
},
133133
{
134-
action: registerConsumerRewardDenomAction{
135-
chain: chainID("provi"),
136-
from: validatorID("bob"),
137-
denom: "ibc/3C3D7B3BE4ECC85A0E5B52A3AEC3B7DFC2AA9CA47C37821E57020D6807043BE9",
134+
action: submitChangeRewardDenomsProposalAction{
135+
denom: "ibc/3C3D7B3BE4ECC85A0E5B52A3AEC3B7DFC2AA9CA47C37821E57020D6807043BE9",
136+
deposit: 10000001,
137+
from: validatorID("bob"),
138+
},
139+
state: State{
140+
chainID("provi"): ChainState{
141+
// Denom not yet registered, gov prop needs to pass first
142+
RegisteredConsumerRewardDenoms: &[]string{},
143+
},
144+
},
145+
},
146+
{
147+
action: voteGovProposalAction{
148+
chain: chainID("provi"),
149+
from: []validatorID{validatorID("alice"), validatorID("bob"), validatorID("carol")},
150+
vote: []string{"yes", "yes", "yes"},
151+
propNumber: 2,
138152
},
139153
state: State{
140154
chainID("provi"): ChainState{
141155
// Check that the denom is registered on provider chain
142156
RegisteredConsumerRewardDenoms: &[]string{"ibc/3C3D7B3BE4ECC85A0E5B52A3AEC3B7DFC2AA9CA47C37821E57020D6807043BE9"},
143-
ValBalances: &map[validatorID]uint{
144-
// make sure that bob's account was debited
145-
validatorID("bob"): 9490000000,
146-
},
147157
},
148158
},
149159
},

tests/integration/distribution.go

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
sdk "github.com/cosmos/cosmos-sdk/types"
99
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
10-
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
1110

1211
icstestingutils "github.com/cosmos/interchain-security/v3/testutil/integration"
1312
consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper"
@@ -96,41 +95,13 @@ func (s *CCVTestSuite) TestRewardsDistribution() {
9695
// Check that the coins got into the ConsumerRewardsPool
9796
s.Require().True(rewardCoins[ibcCoinIndex].Amount.Equal(providerExpectedRewards[0].Amount))
9897

99-
// Attempt to register the consumer reward denom, but fail because the account has no coins
100-
101-
// Get the balance of delAddr to send it out
102-
senderCoins := providerBankKeeper.GetAllBalances(s.providerCtx(), delAddr)
103-
104-
// Send the coins to the governance module just to have a place to send them
105-
err = providerBankKeeper.SendCoinsFromAccountToModule(s.providerCtx(), delAddr, govtypes.ModuleName, senderCoins)
106-
s.Require().NoError(err)
107-
108-
// Attempt to register the consumer reward denom, but fail because the account has no coins
109-
err = s.providerApp.GetProviderKeeper().RegisterConsumerRewardDenom(s.providerCtx(), rewardCoins[ibcCoinIndex].Denom, delAddr)
110-
s.Require().Error(err)
111-
11298
// Advance a block and check that the coins are still in the ConsumerRewardsPool
11399
s.providerChain.NextBlock()
114100
rewardCoins = providerBankKeeper.GetAllBalances(s.providerCtx(), rewardPool)
115101
s.Require().True(rewardCoins[ibcCoinIndex].Amount.Equal(providerExpectedRewards[0].Amount))
116102

117-
// Successfully register the consumer reward denom this time
118-
119-
// Send the coins back to the delAddr
120-
err = providerBankKeeper.SendCoinsFromModuleToAccount(s.providerCtx(), govtypes.ModuleName, delAddr, senderCoins)
121-
s.Require().NoError(err)
122-
123-
// log the sender's coins
124-
senderCoins1 := providerBankKeeper.GetAllBalances(s.providerCtx(), delAddr)
125-
126-
// Register the consumer reward denom
127-
err = s.providerApp.GetProviderKeeper().RegisterConsumerRewardDenom(s.providerCtx(), rewardCoins[ibcCoinIndex].Denom, delAddr)
128-
s.Require().NoError(err)
129-
130-
// Check that the delAddr has the right amount less money in it after paying the fee
131-
senderCoins2 := providerBankKeeper.GetAllBalances(s.providerCtx(), delAddr)
132-
consumerRewardDenomRegistrationFee := s.providerApp.GetProviderKeeper().GetConsumerRewardDenomRegistrationFee(s.providerCtx())
133-
s.Require().Equal(senderCoins1.Sub(senderCoins2...), sdk.NewCoins(consumerRewardDenomRegistrationFee))
103+
// Set the consumer reward denom. This would be done by a governance proposal in prod
104+
s.providerApp.GetProviderKeeper().SetConsumerRewardDenom(s.providerCtx(), rewardCoins[ibcCoinIndex].Denom)
134105

135106
s.providerChain.NextBlock()
136107

0 commit comments

Comments
 (0)