Skip to content

Commit e614a55

Browse files
authored
feat: reintroduce TopN (#9)
* feat: reintroduce TopN * chore: fix diff formatting * chore: ensure tx.proto has all required fields * fix: restore tx comments * fix: restore tx.proto formatting * fix: reintroduce TopN state functions for keeper.go * fix: reintroduce TopN state functions for keeper.go * fix: finish TopN first pass with testing * fix: update integration tests for TopN * fix: TopN test coverage * fix: all testing * fix: update testing signatures * fix: cleaning up diffs
1 parent 14f9533 commit e614a55

35 files changed

+4901
-590
lines changed

proto/interchain_security/ccv/consumer/v1/genesis.proto

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -81,86 +81,3 @@ message ConsumerPacketDataList {
8181
repeated interchain_security.ccv.v1.ConsumerPacketData list = 1
8282
[ (gogoproto.nullable) = false ];
8383
}
84-
syntax = "proto3";
85-
86-
package interchain_security.ccv.consumer.v1;
87-
88-
option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types";
89-
90-
import "interchain_security/ccv/v1/shared_consumer.proto";
91-
import "ibc/lightclients/tendermint/v1/tendermint.proto";
92-
93-
import "gogoproto/gogo.proto";
94-
import "interchain_security/ccv/v1/wire.proto";
95-
import "google/protobuf/timestamp.proto";
96-
import "tendermint/abci/types.proto";
97-
98-
// GenesisState defines the CCV consumer genesis state
99-
//
100-
// Note: this type is only used on consumer side and references shared types
101-
// with provider
102-
message GenesisState {
103-
// Reserve 5th slot for removed provider_client_state field
104-
reserved 5;
105-
106-
// Reserve 6th slot for removed provider_consensus_state field
107-
reserved 6;
108-
109-
// Reserve 7th slot for removed maturing_packets field
110-
reserved 7;
111-
112-
// Reserve 8th slot for removed initial_val_set field
113-
reserved 8;
114-
115-
// ConsumerParams is a shared type with provider module
116-
interchain_security.ccv.v1.ConsumerParams params = 1
117-
[ (gogoproto.nullable) = false ];
118-
// Client ID of the provider. Empty for a new chain, filled in on restart.
119-
string provider_client_id = 2;
120-
// Channel ID of the provider. Empty for a new chain, filled in on restart.
121-
string provider_channel_id = 3;
122-
// true for new chain, false for chain restart.
123-
bool new_chain = 4;
124-
// HeightToValsetUpdateId nil on new chain, filled in on restart.
125-
repeated HeightToValsetUpdateID height_to_valset_update_id = 9
126-
[ (gogoproto.nullable) = false ];
127-
// OutstandingDowntimes nil on new chain, filled in on restart.
128-
repeated OutstandingDowntime outstanding_downtime_slashing = 10
129-
[ (gogoproto.nullable) = false ];
130-
// PendingConsumerPackets nil on new chain, filled in on restart.
131-
ConsumerPacketDataList pending_consumer_packets = 11
132-
[ (gogoproto.nullable) = false ];
133-
// LastTransmissionBlockHeight nil on new chain, filled in on restart.
134-
LastTransmissionBlockHeight last_transmission_block_height = 12
135-
[ (gogoproto.nullable) = false ];
136-
// flag indicating whether the consumer CCV module starts in pre-CCV state
137-
bool preCCV = 13;
138-
interchain_security.ccv.v1.ProviderInfo provider = 14
139-
[ (gogoproto.nullable) = false ];
140-
}
141-
142-
// HeightValsetUpdateID represents a mapping internal to the consumer CCV module
143-
// which links a block height to each recv valset update id.
144-
message HeightToValsetUpdateID {
145-
uint64 height = 1;
146-
uint64 valset_update_id = 2;
147-
}
148-
149-
// OutstandingDowntime defines the type used internally to the consumer CCV
150-
// module and is used in order to not send multiple slashing requests for
151-
// the same downtime infraction.
152-
message OutstandingDowntime { string validator_consensus_address = 1; }
153-
154-
// LastTransmissionBlockHeight is the last time validator holding
155-
// pools were transmitted to the provider chain. This type is used internally
156-
// to the consumer CCV module.
157-
message LastTransmissionBlockHeight { int64 height = 1; }
158-
159-
// ConsumerPacketDataList is a list of consumer packet data packets.
160-
//
161-
// Note this type is used internally to the consumer CCV module
162-
// for exporting / importing state in InitGenesis and ExportGenesis.
163-
message ConsumerPacketDataList {
164-
repeated interchain_security.ccv.v1.ConsumerPacketData list = 1
165-
[ (gogoproto.nullable) = false ];
166-
}

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,32 @@ message ConsumerAdditionProposal {
8888
// chain. it is most relevant for chains performing a sovereign to consumer
8989
// changeover in order to maintain the existing ibc transfer channel
9090
string distribution_transmission_channel = 14;
91+
// Corresponds to the percentage of validators that have to validate the chain
92+
// under the Top N case. For example, 53 corresponds to a Top 53% chain,
93+
// meaning that the top 53% provider validators by voting power have to
94+
// validate the proposed consumer chain. top_N can either be 0 or any value in
95+
// [50, 100]. A chain can join with top_N == 0 as an Opt In chain, or with
96+
// top_N ∈ [50, 100] as a Top N chain.
97+
uint32 top_N = 15;
98+
// Corresponds to the maximum power (percentage-wise) a validator can have on
99+
// the consumer chain. For instance, if `validators_power_cap` is set to 32,
100+
// it means that no validator can have more than 32% of the voting power on
101+
// the consumer chain. Note that this might not be feasible. For example,
102+
// think of a consumer chain with only 5 validators and with
103+
// `validators_power_cap` set to 10%. In such a scenario, at least one
104+
// validator would need to have more than 20% of the total voting power.
105+
// Therefore, `validators_power_cap` operates on a best-effort basis.
106+
uint32 validators_power_cap = 16;
107+
// Corresponds to the maximum number of validators that can validate a
108+
// consumer chain. Only applicable to Opt In chains. Setting
109+
// `validator_set_cap` on a Top N chain is a no-op.
110+
uint32 validator_set_cap = 17;
111+
// Corresponds to a list of provider consensus addresses of validators that
112+
// are the ONLY ones that can validate the consumer chain.
113+
repeated string allowlist = 18;
114+
// Corresponds to a list of provider consensus addresses of validators that
115+
// CANNOT validate the consumer chain.
116+
repeated string denylist = 19;
91117
}
92118

93119
// ConsumerRemovalProposal is a governance proposal on the provider chain to
@@ -120,6 +146,32 @@ message ConsumerModificationProposal {
120146
string description = 2;
121147
// the chain-id of the consumer chain to be modified
122148
string chain_id = 3;
149+
// Corresponds to the percentage of validators that have to validate the chain
150+
// under the Top N case. For example, 53 corresponds to a Top 53% chain,
151+
// meaning that the top 53% provider validators by voting power have to
152+
// validate the proposed consumer chain. top_N can either be 0 or any value in
153+
// [50, 100]. A chain can join with top_N == 0 as an Opt In chain, or with
154+
// top_N ∈ [50, 100] as a Top N chain.
155+
uint32 top_N = 4;
156+
// Corresponds to the maximum power (percentage-wise) a validator can have on
157+
// the consumer chain. For instance, if `validators_power_cap` is set to 32,
158+
// it means that no validator can have more than 32% of the voting power on
159+
// the consumer chain. Note that this might not be feasible. For example,
160+
// think of a consumer chain with only 5 validators and with
161+
// `validators_power_cap` set to 10%. In such a scenario, at least one
162+
// validator would need to have more than 20% of the total voting power.
163+
// Therefore, `validators_power_cap` operates on a best-effort basis.
164+
uint32 validators_power_cap = 5;
165+
// Corresponds to the maximum number of validators that can validate a
166+
// consumer chain. Only applicable to Opt In chains. Setting
167+
// `validator_set_cap` on a Top N chain is a no-op.
168+
uint32 validator_set_cap = 6;
169+
// Corresponds to a list of provider consensus addresses of validators that
170+
// are the ONLY ones that can validate the consumer chain.
171+
repeated string allowlist = 7;
172+
// Corresponds to a list of provider consensus addresses of validators that
173+
// CANNOT validate the consumer chain.
174+
repeated string denylist = 8;
123175
}
124176

125177
// EquivocationProposal is a governance proposal on the provider chain to

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ service Query {
107107
option (google.api.http).get =
108108
"/interchain_security/ccv/provider/consumer_validators/{chain_id}";
109109
}
110+
111+
// QueryConsumerChainOptedInValidators returns all opted-in validators for a
112+
// given consumer chain
113+
rpc QueryConsumerChainOptedInValidators(
114+
QueryConsumerChainOptedInValidatorsRequest)
115+
returns (QueryConsumerChainOptedInValidatorsResponse) {
116+
option (google.api.http).get =
117+
"/interchain_security/ccv/provider/opted_in_validators/{chain_id}";
118+
}
110119
}
111120

112121
message QueryConsumerGenesisRequest { string chain_id = 1; }
@@ -135,6 +144,12 @@ message QueryConsumerChainStopProposalsResponse {
135144
message Chain {
136145
string chain_id = 1;
137146
string client_id = 2;
147+
// If chain with `chainID` is a Top-N chain, i.e., enforces at least one
148+
// validator to validate chain `chainID`
149+
uint32 top_N = 3;
150+
// If the chain is a Top-N chain, this is the minimum power required to be in
151+
// the top N. Otherwise, this is -1.
152+
int64 min_power_in_top_N = 4;
138153
}
139154

140155
message QueryValidatorConsumerAddrRequest {
@@ -235,3 +250,12 @@ message QueryConsumerValidatorsValidator {
235250
message QueryConsumerValidatorsResponse {
236251
repeated QueryConsumerValidatorsValidator validators = 1;
237252
}
253+
254+
message QueryConsumerChainOptedInValidatorsRequest {
255+
string chain_id = 1;
256+
}
257+
258+
message QueryConsumerChainOptedInValidatorsResponse {
259+
// The consensus addresses of opted-in validators on the provider chain
260+
repeated string validators_provider_addresses = 1;
261+
}

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

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ service Msg {
3030
returns (MsgConsumerAdditionResponse);
3131
rpc ConsumerRemoval(MsgConsumerRemoval) returns (MsgConsumerRemovalResponse);
3232
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
33+
rpc OptIn(MsgOptIn) returns (MsgOptInResponse);
34+
rpc OptOut(MsgOptOut) returns (MsgOptOutResponse);
3335
rpc ConsumerModification(MsgConsumerModification)
3436
returns (MsgConsumerModificationResponse);
3537
}
@@ -161,8 +163,34 @@ message MsgConsumerAddition {
161163
// chain. it is most relevant for chains performing a sovereign to consumer
162164
// changeover in order to maintan the existing ibc transfer channel
163165
string distribution_transmission_channel = 12;
166+
// Corresponds to the percentage of validators that have to validate the chain
167+
// under the Top N case. For example, 53 corresponds to a Top 53% chain,
168+
// meaning that the top 53% provider validators by voting power have to
169+
// validate the proposed consumer chain. top_N can either be 0 or any value in
170+
// [50, 100]. A chain can join with top_N == 0 as an Opt In chain, or with
171+
// top_N ∈ [50, 100] as a Top N chain.
172+
uint32 top_N = 13;
173+
// Corresponds to the maximum power (percentage-wise) a validator can have on
174+
// the consumer chain. For instance, if `validators_power_cap` is set to 32,
175+
// it means that no validator can have more than 32% of the voting power on
176+
// the consumer chain. Note that this might not be feasible. For example,
177+
// think of a consumer chain with only 5 validators and with
178+
// `validators_power_cap` set to 10%. In such a scenario, at least one
179+
// validator would need to have more than 20% of the total voting power.
180+
// Therefore, `validators_power_cap` operates on a best-effort basis.
181+
uint32 validators_power_cap = 14;
182+
// Corresponds to the maximum number of validators that can validate a
183+
// consumer chain. Only applicable to Opt In chains. Setting
184+
// `validator_set_cap` on a Top N chain is a no-op.
185+
uint32 validator_set_cap = 15;
186+
// Corresponds to a list of provider consensus addresses of validators that
187+
// are the ONLY ones that can validate the consumer chain.
188+
repeated string allowlist = 16;
189+
// Corresponds to a list of provider consensus addresses of validators that
190+
// CANNOT validate the consumer chain.
191+
repeated string denylist = 17;
164192
// signer address
165-
string authority = 13 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
193+
string authority = 18 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
166194
}
167195

168196
// MsgConsumerAdditionResponse defines response type for MsgConsumerAddition
@@ -213,6 +241,42 @@ message MsgChangeRewardDenoms {
213241
// messages
214242
message MsgChangeRewardDenomsResponse {}
215243

244+
message MsgOptIn {
245+
option (gogoproto.equal) = false;
246+
option (gogoproto.goproto_getters) = false;
247+
option (cosmos.msg.v1.signer) = "signer";
248+
// the chain id of the consumer chain to opt in to
249+
string chain_id = 1;
250+
// the validator address on the provider
251+
string provider_addr = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ];
252+
// (optional) The consensus public key to use on the consumer in json string
253+
// format corresponding to proto-any, for example
254+
// `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}`.
255+
// This field is optional and can remain empty (i.e., `consumer_key = ""`). A
256+
// validator can always change the consumer public key at a later stage by
257+
// issuing a `MsgAssignConsumerKey` message.
258+
string consumer_key = 3;
259+
// signer address
260+
string signer = 4 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
261+
}
262+
263+
message MsgOptInResponse {}
264+
265+
message MsgOptOut {
266+
option (gogoproto.equal) = false;
267+
option (gogoproto.goproto_getters) = false;
268+
option (cosmos.msg.v1.signer) = "signer";
269+
// the chain id of the consumer chain to opt out from
270+
string chain_id = 1;
271+
// the validator address on the provider
272+
string provider_addr = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ];
273+
// signer address
274+
string signer = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
275+
276+
}
277+
278+
message MsgOptOutResponse {}
279+
216280
// MsgConsumerModification message contains a governance proposal on the
217281
// provider chain to modify a running consumer chain. If it passes, the consumer
218282
// chain's parameters are updated.
@@ -228,8 +292,34 @@ message MsgConsumerModification {
228292
string description = 2;
229293
// the chain-id of the consumer chain to be modified
230294
string chain_id = 3;
295+
// Corresponds to the percentage of validators that have to validate the chain
296+
// under the Top N case. For example, 53 corresponds to a Top 53% chain,
297+
// meaning that the top 53% provider validators by voting power have to
298+
// validate the proposed consumer chain. top_N can either be 0 or any value in
299+
// [50, 100]. A chain can join with top_N == 0 as an Opt In chain, or with
300+
// top_N ∈ [50, 100] as a Top N chain.
301+
uint32 top_N = 4;
302+
// Corresponds to the maximum power (percentage-wise) a validator can have on
303+
// the consumer chain. For instance, if `validators_power_cap` is set to 32,
304+
// it means that no validator can have more than 32% of the voting power on
305+
// the consumer chain. Note that this might not be feasible. For example,
306+
// think of a consumer chain with only 5 validators and with
307+
// `validators_power_cap` set to 10%. In such a scenario, at least one
308+
// validator would need to have more than 20% of the total voting power.
309+
// Therefore, `validators_power_cap` operates on a best-effort basis.
310+
uint32 validators_power_cap = 5;
311+
// Corresponds to the maximum number of validators that can validate a
312+
// consumer chain. Only applicable to Opt In chains. Setting
313+
// `validator_set_cap` on a Top N chain is a no-op.
314+
uint32 validator_set_cap = 6;
315+
// Corresponds to a list of provider consensus addresses of validators that
316+
// are the ONLY ones that can validate the consumer chain.
317+
repeated string allowlist = 7;
318+
// Corresponds to a list of provider consensus addresses of validators that
319+
// CANNOT validate the consumer chain.
320+
repeated string denylist = 8;
231321
// signer address
232-
string authority = 4 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
322+
string authority = 9 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
233323
}
234324

235325
message MsgConsumerModificationResponse {}

proto/interchain_security/ccv/v1/shared_consumer.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ message ConsumerParams {
6262
google.protobuf.Duration unbonding_period = 9
6363
[ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ];
6464

65+
// Reserved for previously used soft_opt_out_threshold field
66+
reserved 10;
67+
6568
// Reward denoms. These are the denominations which are allowed to be sent to
6669
// the provider as rewards.
6770
repeated string reward_denoms = 11;

tests/e2e/actions.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ type SubmitConsumerAdditionProposalAction struct {
260260
SpawnTime uint
261261
InitialHeight clienttypes.Height
262262
DistributionChannel string
263+
TopN uint32
263264
}
264265

265266
func (tr Chain) submitConsumerAdditionProposal(
@@ -284,6 +285,7 @@ func (tr Chain) submitConsumerAdditionProposal(
284285
UnbondingPeriod: params.UnbondingPeriod,
285286
Deposit: fmt.Sprint(action.Deposit) + `stake`,
286287
DistributionTransmissionChannel: action.DistributionChannel,
288+
TopN: action.TopN,
287289
}
288290

289291
bz, err := json.Marshal(prop)

tests/e2e/steps_start_chains.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint
3939
ConsumerChain: ChainID(consumerName),
4040
SpawnTime: 0,
4141
InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1},
42+
TopN: 100, // All validators must validate (100% = Replicated Security)
4243
},
4344
State: State{
4445
ChainID("provi"): ChainState{

testutil/ibc_testing/generic_setup.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp](
135135

136136
prop := testkeeper.GetTestConsumerAdditionProp()
137137
prop.ChainId = chainID
138-
// For Replicated Security, all validators participate (no TopN parameter)
138+
// For Replicated Security with TopN restoration, set TopN to 100 (all validators participate)
139+
prop.Top_N = 100
139140

140141
// NOTE: we cannot use the time.Now() because the coordinator chooses a hardcoded start time
141142
// using time.Now() could set the spawn time to be too far in the past or too far in the future
@@ -150,9 +151,6 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp](
150151
providerKeeper.SetPendingConsumerAdditionProp(providerChain.GetContext(), prop)
151152
props := providerKeeper.GetAllPendingConsumerAdditionProps(providerChain.GetContext())
152153
s.Require().Len(props, 1, "unexpected len consumer addition proposals in AddConsumer")
153-
154-
// For Replicated Security, all validators automatically participate
155-
// No opt-in needed
156154

157155
// commit the state on the provider chain
158156
// and create the client and genesis of consumer

testutil/keeper/unit_test_helpers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ func GetTestConsumerAdditionProp() *providertypes.ConsumerAdditionProposal {
280280
types.DefaultCCVTimeoutPeriod,
281281
types.DefaultTransferTimeoutPeriod,
282282
types.DefaultConsumerUnbondingPeriod,
283+
0, // TopN = 0 for opt-in chain (doesn't require validators for testing)
283284
).(*providertypes.ConsumerAdditionProposal)
284285

285286
return prop

0 commit comments

Comments
 (0)