Skip to content

Commit

Permalink
feat: Add genesis migrations for v6 to v7. The migration migrates the…
Browse files Browse the repository at this point in the history
… solo machine client state definition, removes all solo machine consensus states and removes the localhost client. (#2824)
  • Loading branch information
colin-axner authored Dec 7, 2022
1 parent cbf9fb4 commit e1b1488
Show file tree
Hide file tree
Showing 9 changed files with 414 additions and 391 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Features

* (core/02-client) [\#2824](https://github.com/cosmos/ibc-go/pull/2824) Add genesis migrations for v6 to v7. The migration migrates the solo machine client state definition, removes all solo machine consensus states and removes the localhost client.
* (core/24-host) [\#2856](https://github.com/cosmos/ibc-go/pull/2856) Add `PrefixedClientStorePath` and `PrefixedClientStoreKey` functions to 24-host
* (core/02-client) [\#2819](https://github.com/cosmos/ibc-go/pull/2819) Add automatic in-place store migrations to remove the localhost client and migrate existing solo machine definitions.
* (light-clients/06-solomachine) [\#2826](https://github.com/cosmos/ibc-go/pull/2826) Add `AppModuleBasic` for the 06-solomachine client and remove solo machine type registration from core IBC. Chains must register the `AppModuleBasic` of light clients.
Expand Down
77 changes: 77 additions & 0 deletions modules/core/02-client/migrations/v7/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package v7

import (
"github.com/cosmos/cosmos-sdk/codec"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
"github.com/cosmos/ibc-go/v6/modules/core/exported"
)

// MigrateGenesis accepts an exported IBC client genesis file and migrates it to:
//
// - Update solo machine client state protobuf definition (v2 to v3)
// - Remove all solo machine consensus states
// - Remove localhost client
func MigrateGenesis(clientGenState *clienttypes.GenesisState, cdc codec.ProtoCodecMarshaler) (*clienttypes.GenesisState, error) {
// To prune the client and consensus states, we will create new slices to fill up
// with information we want to keep.
var (
clientsConsensus []clienttypes.ClientConsensusStates
clients []clienttypes.IdentifiedClientState
)

for _, client := range clientGenState.Clients {
clientType, _, err := clienttypes.ParseClientIdentifier(client.ClientId)
if err != nil {
return nil, err
}

switch clientType {
case exported.Solomachine:
var clientState ClientState
if err := cdc.Unmarshal(client.ClientState.Value, &clientState); err != nil {
return nil, sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state")
}

updatedClientState := migrateSolomachine(clientState)

protoAny, err := clienttypes.PackClientState(&updatedClientState)
if err != nil {
return nil, err
}

clients = append(clients, clienttypes.IdentifiedClientState{
ClientId: client.ClientId,
ClientState: protoAny,
})

case Localhost:
// remove localhost client state by not adding client state

default:
// add all other client states
clients = append(clients, client)
}

// iterate consensus states by client
for _, clientConsensusStates := range clientGenState.ClientsConsensus {
// look for consensus states for the current client
if clientConsensusStates.ClientId == client.ClientId {
switch clientType {
case exported.Solomachine, Localhost:
// remove all consensus states for the solo machine and localhost
// do not add to new clientsConsensus

default:
// ensure all consensus states added for other client types
clientsConsensus = append(clientsConsensus, clientConsensusStates)
}
}
}
}

clientGenState.Clients = clients
clientGenState.ClientsConsensus = clientsConsensus
return clientGenState, nil
}
134 changes: 134 additions & 0 deletions modules/core/02-client/migrations/v7/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package v7_test

import (
"encoding/json"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"

ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client"
"github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7"
"github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
host "github.com/cosmos/ibc-go/v6/modules/core/24-host"
ibctesting "github.com/cosmos/ibc-go/v6/testing"
)

func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() {
// create tendermint clients
for i := 0; i < 3; i++ {
path := ibctesting.NewPath(suite.chainA, suite.chainB)

suite.coordinator.SetupClients(path)

err := path.EndpointA.UpdateClient()
suite.Require().NoError(err)

// update a second time to add more state
err = path.EndpointA.UpdateClient()
suite.Require().NoError(err)
}

// create multiple legacy solo machine clients
solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1)
solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4)

clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper)

// manually generate old proto buf definitions and set in genesis
// NOTE: we cannot use 'ExportGenesis' for the solo machines since we are
// using client states and consensus states which do not implement the exported.ClientState
// and exported.ConsensusState interface
var clients []types.IdentifiedClientState
for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} {
clientState := sm.ClientState()

// generate old client state proto definition
legacyClientState := &v7.ClientState{
Sequence: clientState.Sequence,
ConsensusState: &v7.ConsensusState{
PublicKey: clientState.ConsensusState.PublicKey,
Diversifier: clientState.ConsensusState.Diversifier,
Timestamp: clientState.ConsensusState.Timestamp,
},
AllowUpdateAfterProposal: true,
}

// set client state
protoAny, err := codectypes.NewAnyWithValue(legacyClientState)
suite.Require().NoError(err)
suite.Require().NotNil(protoAny)

clients = append(clients, types.IdentifiedClientState{
ClientId: sm.ClientID,
ClientState: protoAny,
})

// set in store for ease of determining expected genesis
clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID)
bz, err := suite.chainA.App.AppCodec().MarshalInterface(legacyClientState)
suite.Require().NoError(err)
clientStore.Set(host.ClientStateKey(), bz)

protoAny, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState)
suite.Require().NoError(err)
suite.Require().NotNil(protoAny)

// obtain marshalled bytes to set in client store
bz, err = suite.chainA.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState)
suite.Require().NoError(err)

var consensusStates []types.ConsensusStateWithHeight

// set consensus states in store and genesis
for i := uint64(0); i < numCreations; i++ {
height := types.NewHeight(1, i)
clientStore.Set(host.ConsensusStateKey(height), bz)
consensusStates = append(consensusStates, types.ConsensusStateWithHeight{
Height: height,
ConsensusState: protoAny,
})
}

clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, types.ClientConsensusStates{
ClientId: sm.ClientID,
ConsensusStates: consensusStates,
})
}

// solo machine clients must come before tendermint in expected
clientGenState.Clients = append(clients, clientGenState.Clients...)

// migrate store get expected genesis
// store migration and genesis migration should produce identical results
// NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients
err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec())
suite.Require().NoError(err)
expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper)

cdc, ok := suite.chainA.App.AppCodec().(codec.ProtoCodecMarshaler)
suite.Require().True(ok)

migrated, err := v7.MigrateGenesis(&clientGenState, cdc)
suite.Require().NoError(err)

bz, err := cdc.MarshalJSON(&expectedClientGenState)
suite.Require().NoError(err)

// Indent the JSON bz correctly.
var jsonObj map[string]interface{}
err = json.Unmarshal(bz, &jsonObj)
suite.Require().NoError(err)
expectedIndentedBz, err := json.MarshalIndent(jsonObj, "", "\t")
suite.Require().NoError(err)

bz, err = cdc.MarshalJSON(migrated)
suite.Require().NoError(err)

// Indent the JSON bz correctly.
err = json.Unmarshal(bz, &jsonObj)
suite.Require().NoError(err)
indentedBz, err := json.MarshalIndent(jsonObj, "", "\t")
suite.Require().NoError(err)

suite.Require().Equal(string(expectedIndentedBz), string(indentedBz))
}
54 changes: 0 additions & 54 deletions modules/core/legacy/v100/genesis.go

This file was deleted.

Loading

0 comments on commit e1b1488

Please sign in to comment.