Skip to content

Commit c2a1c76

Browse files
mpokesainoe
andauthored
fix!: validate initial height and set default values for init consumer params (#2357)
* add initial height validation * set default consumer init params on creation * add changelog entries * add test for evmos like chain ID * fix UTs * set the default RevisionHeight to 1 * fix e2e tests * use temporary patched Hermes version in compability tests --------- Co-authored-by: Simon Noetzlin <[email protected]>
1 parent 21dc2de commit c2a1c76

16 files changed

+166
-33
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- `[x/provider]` Add validation for initial height and set
2+
default values for consumer initialization params.
3+
([\#2357](https://github.com/cosmos/interchain-security/pull/2357))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- `[x/provider]` Add validation for initial height and set
2+
default values for consumer initialization params.
3+
([\#2357](https://github.com/cosmos/interchain-security/pull/2357))

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ RUN go mod tidy
2929
RUN make install
3030

3131
# Get Hermes build
32-
## TODO: import Hermes release from ghcr.io/informalsystems repository when
33-
## a Hermes release contains the patch in
32+
# TODO: import Hermes release from ghcr.io/informalsystems repository when
33+
# a Hermes release contains the patch in
3434
# https://github.com/informalsystems/hermes/pull/4182
3535
FROM --platform=linux/amd64 otacrew/hermes-ics:latest AS hermes-builder
3636

Dockerfile.combined

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ FROM --platform=linux/amd64 ${PROVIDER_IMAGE} AS provider
2424
FROM --platform=linux/amd64 ${CONSUMER_IMAGE} AS consumer
2525

2626
# Get Hermes build
27-
FROM --platform=linux/amd64 ghcr.io/informalsystems/hermes:1.10.2 AS hermes-builder
27+
# TODO: import Hermes release from ghcr.io/informalsystems repository when
28+
# a Hermes release contains the patch in
29+
# https://github.com/informalsystems/hermes/pull/4182
30+
FROM --platform=linux/amd64 otacrew/hermes-ics:latest AS hermes-builder
2831

2932

3033
# Get GoRelayer

tests/e2e/actions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ func (tr Chain) submitConsumerAdditionProposal(
637637
DistributionTransmissionChannel: action.DistributionChannel,
638638
}
639639

640-
consumerId := tr.CreateConsumer(providerChainCfg.ChainId, consumerChainCfg.ChainId, action.From, Metadata, nil, nil)
640+
consumerId := tr.CreateConsumer(providerChainCfg.ChainId, consumerChainCfg.ChainId, action.From, Metadata, &initializationParameters, nil)
641641
authority := "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn"
642642
// Set the new created consumer-id on the chain's config
643643
consumerChainCfg.ConsumerId = consumerId

testutil/keeper/unit_test_helpers.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func SetupForDeleteConsumerChain(t *testing.T, ctx sdk.Context,
223223
t.Helper()
224224

225225
expectations := GetMocksForCreateConsumerClient(ctx, &mocks,
226-
"chainID", clienttypes.NewHeight(4, 5))
226+
"chainID", clienttypes.NewHeight(0, 5))
227227
expectations = append(expectations, GetMocksForSetConsumerChain(ctx, &mocks, "chainID")...)
228228

229229
gomock.InOrder(expectations...)
@@ -286,7 +286,7 @@ func GetTestConsumerMetadata() providertypes.ConsumerMetadata {
286286

287287
func GetTestInitializationParameters() providertypes.ConsumerInitializationParameters {
288288
return providertypes.ConsumerInitializationParameters{
289-
InitialHeight: clienttypes.NewHeight(4, 5),
289+
InitialHeight: clienttypes.NewHeight(0, 5),
290290
GenesisHash: []byte("gen_hash"),
291291
BinaryHash: []byte("bin_hash"),
292292
SpawnTime: time.Now().UTC(),
@@ -323,7 +323,7 @@ func GetTestMsgUpdateConsumer() providertypes.MsgUpdateConsumer {
323323
func GetTestMsgConsumerAddition() providertypes.MsgConsumerAddition {
324324
return providertypes.MsgConsumerAddition{
325325
ChainId: "a ChainId",
326-
InitialHeight: clienttypes.NewHeight(4, 5),
326+
InitialHeight: clienttypes.NewHeight(0, 5),
327327
GenesisHash: []byte(base64.StdEncoding.EncodeToString([]byte("gen_hash"))),
328328
BinaryHash: []byte(base64.StdEncoding.EncodeToString([]byte("bin_hash"))),
329329
SpawnTime: time.Now(),

x/ccv/provider/keeper/consumer_lifecycle_test.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ func TestPrepareConsumerForLaunch(t *testing.T) {
5656

5757
func TestInitializeConsumer(t *testing.T) {
5858
now := time.Now().UTC()
59-
consumerId := "13"
59+
consumerId := CONSUMER_ID
60+
chainId := CONSUMER_CHAIN_ID
6061

6162
testCases := []struct {
6263
name string
@@ -69,6 +70,7 @@ func TestInitializeConsumer(t *testing.T) {
6970
spawnTime: now,
7071
setup: func(pk *providerkeeper.Keeper, ctx sdk.Context, spawnTime time.Time) {
7172
pk.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_REGISTERED)
73+
pk.SetConsumerChainId(ctx, consumerId, chainId)
7274
err := pk.SetConsumerInitializationParameters(ctx, consumerId,
7375
providertypes.ConsumerInitializationParameters{
7476
SpawnTime: spawnTime,
@@ -89,6 +91,7 @@ func TestInitializeConsumer(t *testing.T) {
8991
spawnTime: now,
9092
setup: func(pk *providerkeeper.Keeper, ctx sdk.Context, spawnTime time.Time) {
9193
pk.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED)
94+
pk.SetConsumerChainId(ctx, consumerId, chainId)
9295
err := pk.SetConsumerInitializationParameters(ctx, consumerId,
9396
providertypes.ConsumerInitializationParameters{
9497
SpawnTime: spawnTime,
@@ -110,6 +113,7 @@ func TestInitializeConsumer(t *testing.T) {
110113
spawnTime: now,
111114
setup: func(pk *providerkeeper.Keeper, ctx sdk.Context, spawnTime time.Time) {
112115
pk.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_REGISTERED)
116+
pk.SetConsumerChainId(ctx, consumerId, chainId)
113117
err := pk.SetConsumerInitializationParameters(ctx, consumerId,
114118
providertypes.ConsumerInitializationParameters{
115119
SpawnTime: time.Time{},
@@ -150,7 +154,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) {
150154

151155
initializationParameters := []providertypes.ConsumerInitializationParameters{
152156
{
153-
InitialHeight: clienttypes.NewHeight(3, 4),
157+
InitialHeight: clienttypes.NewHeight(0, 4),
154158
GenesisHash: []byte{},
155159
BinaryHash: []byte{},
156160
SpawnTime: now.Add(-time.Hour * 2).UTC(),
@@ -163,7 +167,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) {
163167
DistributionTransmissionChannel: "",
164168
},
165169
{
166-
InitialHeight: clienttypes.NewHeight(3, 4),
170+
InitialHeight: clienttypes.NewHeight(0, 4),
167171
GenesisHash: []byte{},
168172
BinaryHash: []byte{},
169173
SpawnTime: now.Add(-time.Hour).UTC(),
@@ -176,7 +180,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) {
176180
DistributionTransmissionChannel: "",
177181
},
178182
{
179-
InitialHeight: clienttypes.NewHeight(3, 4),
183+
InitialHeight: clienttypes.NewHeight(0, 4),
180184
GenesisHash: []byte{},
181185
BinaryHash: []byte{},
182186
SpawnTime: now.Add(time.Hour).UTC(),
@@ -189,7 +193,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) {
189193
DistributionTransmissionChannel: "",
190194
},
191195
{
192-
InitialHeight: clienttypes.NewHeight(3, 4),
196+
InitialHeight: clienttypes.NewHeight(0, 4),
193197
GenesisHash: []byte{},
194198
BinaryHash: []byte{},
195199
SpawnTime: now.Add(-time.Hour).UTC(),
@@ -202,7 +206,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) {
202206
DistributionTransmissionChannel: "",
203207
},
204208
{
205-
InitialHeight: clienttypes.NewHeight(3, 4),
209+
InitialHeight: clienttypes.NewHeight(0, 4),
206210
GenesisHash: []byte{},
207211
BinaryHash: []byte{},
208212
SpawnTime: now.Add(-time.Minute).UTC(),
@@ -283,11 +287,11 @@ func TestBeginBlockLaunchConsumers(t *testing.T) {
283287

284288
// Expect genesis and client creation for only the first, second, and fifth chains (spawn time already passed and valid)
285289
expectedCalls := testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, time.Hour)
286-
expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain0", clienttypes.NewHeight(3, 4))...)
290+
expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain0", clienttypes.NewHeight(0, 4))...)
287291
expectedCalls = append(expectedCalls, testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, time.Hour)...)
288-
expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain1", clienttypes.NewHeight(3, 4))...)
292+
expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain1", clienttypes.NewHeight(0, 4))...)
289293
expectedCalls = append(expectedCalls, testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, time.Hour)...)
290-
expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain3", clienttypes.NewHeight(3, 4))...)
294+
expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain3", clienttypes.NewHeight(0, 4))...)
291295

292296
gomock.InOrder(expectedCalls...)
293297

@@ -479,7 +483,7 @@ func TestCreateConsumerClient(t *testing.T) {
479483

480484
// Valid client creation is asserted with mock expectations here
481485
gomock.InOrder(
482-
testkeeper.GetMocksForCreateConsumerClient(ctx, mocks, CONSUMER_CHAIN_ID, clienttypes.NewHeight(4, 5))...,
486+
testkeeper.GetMocksForCreateConsumerClient(ctx, mocks, CONSUMER_CHAIN_ID, clienttypes.NewHeight(0, 5))...,
483487
)
484488
},
485489
expClientCreated: true,
@@ -774,7 +778,7 @@ func TestBeginBlockStopConsumers(t *testing.T) {
774778
chainId := chainIds[i]
775779
// A consumer chain is setup corresponding to each consumerId, making these mocks necessary
776780
expectations = append(expectations, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks,
777-
chainId, clienttypes.NewHeight(2, 3))...)
781+
chainId, clienttypes.NewHeight(0, 3))...)
778782
expectations = append(expectations, testkeeper.GetMocksForSetConsumerChain(ctx, &mocks, chainId)...)
779783
}
780784
// Only first two consumer chains should be stopped
@@ -789,7 +793,7 @@ func TestBeginBlockStopConsumers(t *testing.T) {
789793
for i, consumerId := range consumerIds {
790794
// Setup a valid consumer chain for each consumerId
791795
initializationRecord := testkeeper.GetTestInitializationParameters()
792-
initializationRecord.InitialHeight = clienttypes.NewHeight(2, 3)
796+
initializationRecord.InitialHeight = clienttypes.NewHeight(0, 3)
793797
registrationRecord := testkeeper.GetTestConsumerMetadata()
794798

795799
providerKeeper.SetConsumerChainId(ctx, consumerId, chainIds[i])

x/ccv/provider/keeper/grpc_query_test.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,15 @@ func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) {
312312

313313
// set up some consumer chains
314314
for i := 0; i < consumerNum; i++ {
315-
chainID := "consumer-" + strconv.Itoa(i)
315+
revisionNumber := i + 1
316+
chainID := "consumer-" + strconv.Itoa(revisionNumber)
316317
metadata := types.ConsumerMetadata{Name: chainID}
318+
initializationParameters := types.DefaultConsumerInitializationParameters()
319+
initializationParameters.InitialHeight.RevisionNumber = uint64(revisionNumber)
317320
msg := types.MsgCreateConsumer{
318-
ChainId: chainID,
319-
Metadata: metadata,
321+
ChainId: chainID,
322+
Metadata: metadata,
323+
InitializationParameters: &initializationParameters,
320324
}
321325
resp, err := msgServer.CreateConsumer(ctx, &msg)
322326
require.NoError(t, err)
@@ -535,7 +539,7 @@ func TestQueryConsumerChain(t *testing.T) {
535539
defer ctrl.Finish()
536540

537541
consumerId := "0"
538-
chainId := "consumer-1"
542+
chainId := "consumer"
539543

540544
req := types.QueryConsumerChainRequest{
541545
ConsumerId: consumerId,
@@ -644,11 +648,15 @@ func TestQueryConsumerChains(t *testing.T) {
644648

645649
// create four consumer chains in different phase
646650
for i := 0; i < consumerNum; i++ {
647-
chainID := "consumer-" + strconv.Itoa(i)
651+
revisionNumber := i + 1
652+
chainID := "consumer-" + strconv.Itoa(revisionNumber)
648653
metadata := types.ConsumerMetadata{Name: chainID}
654+
initializationParameters := types.DefaultConsumerInitializationParameters()
655+
initializationParameters.InitialHeight.RevisionNumber = uint64(revisionNumber)
649656
msg := types.MsgCreateConsumer{
650-
ChainId: chainID,
651-
Metadata: metadata,
657+
ChainId: chainID,
658+
Metadata: metadata,
659+
InitializationParameters: &initializationParameters,
652660
}
653661
resp, err := msgServer.CreateConsumer(ctx, &msg)
654662
require.NoError(t, err)

x/ccv/provider/keeper/msg_server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ func (k msgServer) CreateConsumer(goCtx context.Context, msg *types.MsgCreateCon
378378

379379
// initialization parameters are optional and hence could be nil;
380380
// in that case, set the default
381-
initializationParameters := types.ConsumerInitializationParameters{} // default params
381+
initializationParameters := types.DefaultConsumerInitializationParameters() // default params
382382
if msg.InitializationParameters != nil {
383383
initializationParameters = *msg.InitializationParameters
384384
}

x/ccv/provider/keeper/msg_server_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func TestUpdateConsumer(t *testing.T) {
8484
// create a chain before updating it
8585
createConsumerResponse, err := msgServer.CreateConsumer(ctx,
8686
&providertypes.MsgCreateConsumer{
87-
Submitter: "submitter", ChainId: "chainId",
87+
Submitter: "submitter", ChainId: "chainId-1",
8888
Metadata: providertypes.ConsumerMetadata{
8989
Name: "name",
9090
Description: "description",
@@ -113,6 +113,7 @@ func TestUpdateConsumer(t *testing.T) {
113113
}
114114

115115
expectedInitializationParameters := testkeeper.GetTestInitializationParameters()
116+
expectedInitializationParameters.InitialHeight.RevisionNumber = 1
116117
expectedPowerShapingParameters := testkeeper.GetTestPowerShapingParameters()
117118

118119
expectedOwnerAddress := "cosmos1dkas8mu4kyhl5jrh4nzvm65qz588hy9qcz08la"

x/ccv/provider/keeper/permissionless.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ func (k Keeper) SetConsumerInitializationParameters(ctx sdk.Context, consumerId
134134
if err != nil {
135135
return fmt.Errorf("failed to marshal initialization parameters (%+v) for consumer id (%s): %w", parameters, consumerId, err)
136136
}
137+
chainId, err := k.GetConsumerChainId(ctx, consumerId)
138+
if err != nil {
139+
return fmt.Errorf("failed to get consumer chain ID for consumer id (%s): %w", consumerId, err)
140+
}
141+
// validate that the initial height matches the chain ID
142+
if err := types.ValidateInitialHeight(parameters.InitialHeight, chainId); err != nil {
143+
return fmt.Errorf("invalid initial height for consumer id (%s): %w", consumerId, err)
144+
}
137145
store.Set(types.ConsumerIdToInitializationParametersKey(consumerId), bz)
138146
return nil
139147
}

x/ccv/provider/keeper/permissionless_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,16 @@ func TestConsumerInitializationParameters(t *testing.T) {
150150
HistoricalEntries: 456,
151151
DistributionTransmissionChannel: "distribution_transmission_channel",
152152
}
153-
providerKeeper.SetConsumerInitializationParameters(ctx, CONSUMER_ID, expectedInitializationParameters)
153+
providerKeeper.SetConsumerChainId(ctx, CONSUMER_ID, "chain-1")
154+
err = providerKeeper.SetConsumerInitializationParameters(ctx, CONSUMER_ID, expectedInitializationParameters)
155+
require.NoError(t, err)
154156
actualInitializationParameters, err := providerKeeper.GetConsumerInitializationParameters(ctx, CONSUMER_ID)
155157
require.NoError(t, err)
156158
require.Equal(t, expectedInitializationParameters, actualInitializationParameters)
157159

158160
// assert that overwriting the current initialization record works
159161
expectedInitializationParameters = providertypes.ConsumerInitializationParameters{
160-
InitialHeight: types.Height{RevisionNumber: 2, RevisionHeight: 3},
162+
InitialHeight: types.Height{RevisionNumber: 1, RevisionHeight: 3},
161163
GenesisHash: []byte{2, 3},
162164
BinaryHash: []byte{4, 5},
163165
SpawnTime: time.Unix(2, 3).UTC(),

x/ccv/provider/types/msg.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package types
33
import (
44
"encoding/json"
55
"fmt"
6-
"github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
76
"strings"
87

8+
"github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
9+
10+
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
911
ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint"
1012

1113
errorsmod "cosmossdk.io/errors"
@@ -620,3 +622,11 @@ func validateProviderAddress(addr, signer string) error {
620622

621623
return nil
622624
}
625+
626+
func ValidateInitialHeight(initialHeight clienttypes.Height, chainID string) error {
627+
revision := clienttypes.ParseChainID(chainID)
628+
if initialHeight.RevisionNumber != revision {
629+
return fmt.Errorf("chain ID (%s) doesn't match revision number (%d)", chainID, initialHeight.RevisionNumber)
630+
}
631+
return nil
632+
}

x/ccv/provider/types/msg_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,3 +647,66 @@ func TestMsgAssignConsumerKeyValidateBasic(t *testing.T) {
647647
})
648648
}
649649
}
650+
651+
func TestValidateInitialHeight(t *testing.T) {
652+
testCases := []struct {
653+
name string
654+
chainId string
655+
initialHeight clienttypes.Height
656+
expPass bool
657+
}{
658+
{
659+
name: "valid with revision number",
660+
chainId: "chain-1",
661+
initialHeight: clienttypes.Height{
662+
RevisionNumber: 1,
663+
RevisionHeight: 0,
664+
},
665+
expPass: true,
666+
},
667+
{
668+
name: "valid without revision number",
669+
chainId: "chain",
670+
initialHeight: clienttypes.Height{
671+
RevisionNumber: 0,
672+
RevisionHeight: 0,
673+
},
674+
expPass: true,
675+
},
676+
{
677+
name: "invalid without revision number",
678+
chainId: "chain",
679+
initialHeight: clienttypes.Height{
680+
RevisionNumber: 1,
681+
RevisionHeight: 0,
682+
},
683+
expPass: false,
684+
},
685+
{
686+
name: "invalid without revision number",
687+
chainId: "chain-1",
688+
initialHeight: clienttypes.Height{
689+
RevisionNumber: 0,
690+
RevisionHeight: 0,
691+
},
692+
expPass: false,
693+
},
694+
{
695+
name: "valid: evmos-like chain IDs",
696+
chainId: "evmos_9001-2",
697+
initialHeight: clienttypes.Height{
698+
RevisionNumber: 2,
699+
RevisionHeight: 0,
700+
},
701+
expPass: true,
702+
},
703+
}
704+
for _, tc := range testCases {
705+
err := types.ValidateInitialHeight(tc.initialHeight, tc.chainId)
706+
if tc.expPass {
707+
require.NoError(t, err, "valid case: '%s' should not return error. got %w", tc.name, err)
708+
} else {
709+
require.Error(t, err, "invalid case: '%s' must return error but got none", tc.name)
710+
}
711+
}
712+
}

0 commit comments

Comments
 (0)