ibc/02-client: import export GenesisState (#6073)
* ibc/02-client: import export GenesisState * client validation * genesis validation * ibc genesis tests * GetAllConsensusStates test * fix non-determinism * lint * fix test
This commit is contained in:
parent
f0b72b93be
commit
9b51908597
|
@ -37,6 +37,9 @@ var (
|
|||
ErrRootNotFound = types.ErrRootNotFound
|
||||
ErrInvalidHeader = types.ErrInvalidHeader
|
||||
ErrInvalidEvidence = types.ErrInvalidEvidence
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
NewGenesisState = types.NewGenesisState
|
||||
NewClientConsensusStates = types.NewClientConsensusStates
|
||||
|
||||
// variable aliases
|
||||
SubModuleCdc = types.SubModuleCdc
|
||||
|
@ -45,7 +48,10 @@ var (
|
|||
AttributeValueCategory = types.AttributeValueCategory
|
||||
)
|
||||
|
||||
// nolint
|
||||
type (
|
||||
Keeper = keeper.Keeper
|
||||
StakingKeeper = types.StakingKeeper
|
||||
Keeper = keeper.Keeper
|
||||
StakingKeeper = types.StakingKeeper
|
||||
GenesisState = types.GenesisState
|
||||
ClientConsensusStates = types.ClientConsensusStates
|
||||
)
|
||||
|
|
|
@ -20,6 +20,7 @@ type ClientState interface {
|
|||
ClientType() ClientType
|
||||
GetLatestHeight() uint64
|
||||
IsFrozen() bool
|
||||
Validate() error
|
||||
|
||||
// State verification functions
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// InitGenesis initializes the ibc client submodule's state from a provided genesis
|
||||
// state.
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, gs GenesisState) {
|
||||
for _, client := range gs.Clients {
|
||||
k.SetClientState(ctx, client)
|
||||
k.SetClientType(ctx, client.GetID(), client.ClientType())
|
||||
}
|
||||
for _, cs := range gs.ClientsConsensus {
|
||||
for _, consState := range cs.ConsensusStates {
|
||||
k.SetClientConsensusState(ctx, cs.ClientID, consState.GetHeight(), consState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExportGenesis returns the ibc client submodule's exported genesis.
|
||||
func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
return GenesisState{
|
||||
Clients: k.GetAllClients(ctx),
|
||||
ClientsConsensus: k.GetAllConsensusStates(ctx),
|
||||
}
|
||||
}
|
|
@ -98,6 +98,67 @@ func (k Keeper) SetClientConsensusState(ctx sdk.Context, clientID string, height
|
|||
store.Set(ibctypes.KeyConsensusState(height), bz)
|
||||
}
|
||||
|
||||
// IterateConsensusStates provides an iterator over all stored consensus states.
|
||||
// objects. For each State object, cb will be called. If the cb returns true,
|
||||
// the iterator will close and stop.
|
||||
func (k Keeper) IterateConsensusStates(ctx sdk.Context, cb func(clientID string, cs exported.ConsensusState) bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ibctypes.KeyClientStorePrefix)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
keySplit := strings.Split(string(iterator.Key()), "/")
|
||||
// consensus key is in the format "clients/<clientID>/consensusState/<height>"
|
||||
if len(keySplit) != 4 || keySplit[2] != "consensusState" {
|
||||
continue
|
||||
}
|
||||
clientID := keySplit[1]
|
||||
var consensusState exported.ConsensusState
|
||||
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &consensusState)
|
||||
|
||||
if cb(clientID, consensusState) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllConsensusStates returns all stored client consensus states.
|
||||
// NOTE: non deterministic.
|
||||
func (k Keeper) GetAllConsensusStates(ctx sdk.Context) (clientConsStates []types.ClientConsensusStates) {
|
||||
var clientIDs []string
|
||||
// create map to add consensus states to the existing clients
|
||||
cons := make(map[string][]exported.ConsensusState)
|
||||
|
||||
k.IterateConsensusStates(ctx, func(clientID string, cs exported.ConsensusState) bool {
|
||||
consensusStates, ok := cons[clientID]
|
||||
if !ok {
|
||||
clientIDs = append(clientIDs, clientID)
|
||||
cons[clientID] = []exported.ConsensusState{cs}
|
||||
return false
|
||||
}
|
||||
|
||||
cons[clientID] = append(consensusStates, cs)
|
||||
return false
|
||||
})
|
||||
|
||||
// create ClientConsensusStates in the same order of iteration to prevent non-determinism
|
||||
for len(clientIDs) > 0 {
|
||||
id := clientIDs[len(clientIDs)-1]
|
||||
consensusStates, ok := cons[id]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("consensus states from client id %s not found", id))
|
||||
}
|
||||
|
||||
clientConsState := types.NewClientConsensusStates(id, consensusStates)
|
||||
clientConsStates = append(clientConsStates, clientConsState)
|
||||
|
||||
// remove the last element
|
||||
clientIDs = clientIDs[:len(clientIDs)-1]
|
||||
}
|
||||
|
||||
return clientConsStates
|
||||
}
|
||||
|
||||
// HasClientConsensusState returns if keeper has a ConsensusState for a particular
|
||||
// client at the given height
|
||||
func (k Keeper) HasClientConsensusState(ctx sdk.Context, clientID string, height uint64) bool {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
|
||||
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
|
@ -161,7 +162,9 @@ func (suite KeeperTestSuite) TestGetConsensusState() {
|
|||
|
||||
func (suite KeeperTestSuite) TestConsensusStateHelpers() {
|
||||
// initial setup
|
||||
clientState, _ := ibctmtypes.Initialize(testClientID, trustingPeriod, ubdPeriod, maxClockDrift, suite.header)
|
||||
clientState, err := ibctmtypes.Initialize(testClientID, trustingPeriod, ubdPeriod, maxClockDrift, suite.header)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.keeper.SetClientState(suite.ctx, clientState)
|
||||
suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState)
|
||||
|
||||
|
@ -192,3 +195,37 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() {
|
|||
suite.Require().True(ok)
|
||||
suite.Require().Equal(suite.consensusState, lte, "LTE helper function did not return latest client state below height: %d", testClientHeight+3)
|
||||
}
|
||||
|
||||
func (suite KeeperTestSuite) TestGetAllConsensusStates() {
|
||||
expConsensus := []types.ClientConsensusStates{
|
||||
types.NewClientConsensusStates(
|
||||
testClientID,
|
||||
[]exported.ConsensusState{
|
||||
ibctmtypes.NewConsensusState(
|
||||
suite.consensusState.Timestamp, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.consensusState.GetHeight(), &tmtypes.ValidatorSet{},
|
||||
),
|
||||
ibctmtypes.NewConsensusState(
|
||||
suite.consensusState.Timestamp.Add(time.Minute), commitmenttypes.NewMerkleRoot([]byte("app_hash")), suite.consensusState.GetHeight()+1, &tmtypes.ValidatorSet{},
|
||||
),
|
||||
},
|
||||
),
|
||||
types.NewClientConsensusStates(
|
||||
testClientID2,
|
||||
[]exported.ConsensusState{
|
||||
ibctmtypes.NewConsensusState(
|
||||
suite.consensusState.Timestamp.Add(2*time.Minute), commitmenttypes.NewMerkleRoot([]byte("app_hash_2")), suite.consensusState.GetHeight()+2, &tmtypes.ValidatorSet{},
|
||||
),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
for i := range expConsensus {
|
||||
for _, cons := range expConsensus[i].ConsensusStates {
|
||||
suite.keeper.SetClientConsensusState(suite.ctx, expConsensus[i].ClientID, cons.GetHeight(), cons)
|
||||
}
|
||||
}
|
||||
|
||||
consStates := suite.keeper.GetAllConsensusStates(suite.ctx)
|
||||
suite.Require().Len(consStates, len(expConsensus))
|
||||
suite.Require().Equal(expConsensus, consStates)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
|
||||
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
|
||||
)
|
||||
|
||||
// ClientConsensusStates defines all the stored consensus states for a given client.
|
||||
type ClientConsensusStates struct {
|
||||
ClientID string `json:"client_id" yaml:"client_id"`
|
||||
ConsensusStates []exported.ConsensusState `json:"consensus_states" yaml:"consensus_states"`
|
||||
}
|
||||
|
||||
// NewClientConsensusStates creates a new ClientConsensusStates instance.
|
||||
func NewClientConsensusStates(id string, states []exported.ConsensusState) ClientConsensusStates {
|
||||
return ClientConsensusStates{
|
||||
ClientID: id,
|
||||
ConsensusStates: states,
|
||||
}
|
||||
}
|
||||
|
||||
// GenesisState defines the ibc client submodule's genesis state.
|
||||
type GenesisState struct {
|
||||
Clients []exported.ClientState `json:"clients" yaml:"clients"`
|
||||
ClientsConsensus []ClientConsensusStates `json:"clients_consensus" yaml:"clients_consensus"`
|
||||
}
|
||||
|
||||
// NewGenesisState creates a GenesisState instance.
|
||||
func NewGenesisState(
|
||||
clients []exported.ClientState, clientsConsensus []ClientConsensusStates,
|
||||
) GenesisState {
|
||||
return GenesisState{
|
||||
Clients: clients,
|
||||
ClientsConsensus: clientsConsensus,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultGenesisState returns the ibc client submodule's default genesis state.
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Clients: []exported.ClientState{},
|
||||
ClientsConsensus: []ClientConsensusStates{},
|
||||
}
|
||||
}
|
||||
|
||||
// Validate performs basic genesis state validation returning an error upon any
|
||||
// failure.
|
||||
func (gs GenesisState) Validate() error {
|
||||
for i, client := range gs.Clients {
|
||||
if err := client.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid client %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, cs := range gs.ClientsConsensus {
|
||||
if err := host.DefaultClientIdentifierValidator(cs.ClientID); err != nil {
|
||||
return fmt.Errorf("invalid client consensus state %d: %w", i, err)
|
||||
}
|
||||
for _, consensusState := range cs.ConsensusStates {
|
||||
if err := consensusState.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("invalid client consensus state %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package types_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/cachekv"
|
||||
"github.com/cosmos/cosmos-sdk/store/dbadapter"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
|
||||
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
|
||||
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
|
||||
)
|
||||
|
||||
const (
|
||||
clientID = "ethbridge"
|
||||
|
||||
trustingPeriod time.Duration = time.Hour * 24 * 7 * 2
|
||||
ubdPeriod time.Duration = time.Hour * 24 * 7 * 3
|
||||
maxClockDrift time.Duration = time.Second * 10
|
||||
)
|
||||
|
||||
func TestValidateGenesis(t *testing.T) {
|
||||
privVal := tmtypes.NewMockPV()
|
||||
pubKey, err := privVal.GetPubKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
val := tmtypes.NewValidator(pubKey, 10)
|
||||
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val})
|
||||
|
||||
mem := dbadapter.Store{DB: dbm.NewMemDB()}
|
||||
store := cachekv.NewStore(mem)
|
||||
header := ibctmtypes.CreateTestHeader("chainID", 10, now, valSet, []tmtypes.PrivValidator{privVal})
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
genState types.GenesisState
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
genState: types.DefaultGenesisState(),
|
||||
expPass: true,
|
||||
},
|
||||
{
|
||||
name: "valid genesis",
|
||||
genState: types.NewGenesisState(
|
||||
[]exported.ClientState{
|
||||
ibctmtypes.NewClientState(clientID, trustingPeriod, ubdPeriod, maxClockDrift, header),
|
||||
localhosttypes.NewClientState(store, "chaindID", 10),
|
||||
},
|
||||
[]types.ClientConsensusStates{
|
||||
{
|
||||
clientID,
|
||||
[]exported.ConsensusState{
|
||||
ibctmtypes.NewConsensusState(
|
||||
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.ValidatorSet,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
expPass: true,
|
||||
},
|
||||
{
|
||||
name: "invalid client",
|
||||
genState: types.NewGenesisState(
|
||||
[]exported.ClientState{
|
||||
ibctmtypes.NewClientState(clientID, trustingPeriod, ubdPeriod, maxClockDrift, header),
|
||||
localhosttypes.NewClientState(store, "chaindID", 0),
|
||||
},
|
||||
nil,
|
||||
),
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid consensus state",
|
||||
genState: types.NewGenesisState(
|
||||
[]exported.ClientState{
|
||||
ibctmtypes.NewClientState(clientID, trustingPeriod, ubdPeriod, maxClockDrift, header),
|
||||
localhosttypes.NewClientState(store, "chaindID", 10),
|
||||
},
|
||||
[]types.ClientConsensusStates{
|
||||
{
|
||||
"CLIENTID2",
|
||||
[]exported.ConsensusState{
|
||||
ibctmtypes.NewConsensusState(
|
||||
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.ValidatorSet,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid consensus state",
|
||||
genState: types.NewGenesisState(
|
||||
[]exported.ClientState{
|
||||
ibctmtypes.NewClientState(clientID, trustingPeriod, ubdPeriod, maxClockDrift, header),
|
||||
localhosttypes.NewClientState(store, "chaindID", 10),
|
||||
},
|
||||
[]types.ClientConsensusStates{
|
||||
types.NewClientConsensusStates(
|
||||
clientID,
|
||||
[]exported.ConsensusState{
|
||||
ibctmtypes.NewConsensusState(
|
||||
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.ValidatorSet,
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
expPass: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
err := tc.genState.Validate()
|
||||
if tc.expPass {
|
||||
require.NoError(t, err, tc.name)
|
||||
} else {
|
||||
require.Error(t, err, tc.name)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import (
|
|||
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
|
||||
commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported"
|
||||
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
|
||||
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
|
||||
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
|
||||
)
|
||||
|
||||
|
@ -87,7 +88,10 @@ func (cs ClientState) GetID() string {
|
|||
|
||||
// GetChainID returns the chain-id from the last header
|
||||
func (cs ClientState) GetChainID() string {
|
||||
return cs.LastHeader.ChainID
|
||||
if cs.LastHeader.SignedHeader.Header == nil {
|
||||
return ""
|
||||
}
|
||||
return cs.LastHeader.SignedHeader.Header.ChainID
|
||||
}
|
||||
|
||||
// ClientType is tendermint.
|
||||
|
@ -110,6 +114,23 @@ func (cs ClientState) IsFrozen() bool {
|
|||
return cs.FrozenHeight != 0
|
||||
}
|
||||
|
||||
// Validate performs a basic validation of the client state fields.
|
||||
func (cs ClientState) Validate() error {
|
||||
if err := host.DefaultClientIdentifierValidator(cs.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if cs.TrustingPeriod == 0 {
|
||||
return errors.New("trusting period cannot be zero")
|
||||
}
|
||||
if cs.UnbondingPeriod == 0 {
|
||||
return errors.New("unbonding period cannot be zero")
|
||||
}
|
||||
if cs.MaxClockDrift == 0 {
|
||||
return errors.New("max clock drift cannot be zero")
|
||||
}
|
||||
return cs.LastHeader.ValidateBasic(cs.GetChainID())
|
||||
}
|
||||
|
||||
// VerifyClientConsensusState verifies a proof of the consensus state of the
|
||||
// Tendermint client stored on the target machine.
|
||||
func (cs ClientState) VerifyClientConsensusState(
|
||||
|
|
|
@ -5,17 +5,67 @@ import (
|
|||
connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported"
|
||||
channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel"
|
||||
channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
|
||||
)
|
||||
|
||||
const (
|
||||
testClientID = "clientidone"
|
||||
testConnectionID = "connectionid"
|
||||
testPortID = "testportid"
|
||||
testChannelID = "testchannelid"
|
||||
testSequence = 1
|
||||
)
|
||||
|
||||
func (suite *TendermintTestSuite) TestValidate() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
clientState types.ClientState
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
name: "valid client",
|
||||
clientState: ibctmtypes.NewClientState(testClientID, trustingPeriod, ubdPeriod, maxClockDrift, suite.header),
|
||||
expPass: true,
|
||||
},
|
||||
{
|
||||
name: "invalid client id",
|
||||
clientState: ibctmtypes.NewClientState("testClientID", trustingPeriod, ubdPeriod, maxClockDrift, suite.header),
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid trusting period",
|
||||
clientState: ibctmtypes.NewClientState(testClientID, 0, ubdPeriod, maxClockDrift, suite.header),
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid unbonding period",
|
||||
clientState: ibctmtypes.NewClientState(testClientID, trustingPeriod, 0, maxClockDrift, suite.header),
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid max clock drift",
|
||||
clientState: ibctmtypes.NewClientState(testClientID, trustingPeriod, ubdPeriod, 0, suite.header),
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid header",
|
||||
clientState: ibctmtypes.NewClientState(testClientID, trustingPeriod, ubdPeriod, maxClockDrift, ibctmtypes.Header{}),
|
||||
expPass: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := tc.clientState.Validate()
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err, tc.name)
|
||||
} else {
|
||||
suite.Require().Error(err, tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
|
|
@ -19,6 +19,19 @@ type ConsensusState struct {
|
|||
ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"`
|
||||
}
|
||||
|
||||
// NewConsensusState creates a new ConsensusState instance.
|
||||
func NewConsensusState(
|
||||
timestamp time.Time, root commitmentexported.Root, height uint64,
|
||||
valset *tmtypes.ValidatorSet,
|
||||
) ConsensusState {
|
||||
return ConsensusState{
|
||||
Timestamp: timestamp,
|
||||
Root: root,
|
||||
Height: height,
|
||||
ValidatorSet: valset,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientType returns Tendermint
|
||||
func (ConsensusState) ClientType() clientexported.ClientType {
|
||||
return clientexported.Tendermint
|
||||
|
|
|
@ -73,6 +73,9 @@ func (msg MsgCreateClient) ValidateBasic() error {
|
|||
if msg.Signer.Empty() {
|
||||
return sdkerrors.ErrInvalidAddress
|
||||
}
|
||||
if msg.Header.SignedHeader.Header == nil {
|
||||
return sdkerrors.Wrap(ErrInvalidHeader, "header cannot be nil")
|
||||
}
|
||||
// ValidateBasic of provided header with self-attested chain-id
|
||||
if err := msg.Header.ValidateBasic(msg.Header.ChainID); err != nil {
|
||||
return sdkerrors.Wrapf(ErrInvalidHeader, "header failed validatebasic with its own chain-id: %v", err)
|
||||
|
|
|
@ -19,18 +19,16 @@ func (suite *TendermintTestSuite) TestMsgCreateClientValidateBasic() {
|
|||
}{
|
||||
{ibctmtypes.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, trustingPeriod, ubdPeriod, maxClockDrift, signer), true, "success msg should pass"},
|
||||
{ibctmtypes.NewMsgCreateClient("BADCHAIN", suite.header, trustingPeriod, ubdPeriod, maxClockDrift, signer), false, "invalid client id passed"},
|
||||
{ibctmtypes.NewMsgCreateClient("goodchain", suite.header, trustingPeriod, ubdPeriod, maxClockDrift, signer), false, "unregistered client type passed"},
|
||||
{ibctmtypes.NewMsgCreateClient("goodchain", suite.header, trustingPeriod, ubdPeriod, maxClockDrift, signer), false, "invalid Consensus State in msg passed"},
|
||||
{ibctmtypes.NewMsgCreateClient("goodchain", suite.header, 0, ubdPeriod, maxClockDrift, signer), false, "zero trusting period passed"},
|
||||
{ibctmtypes.NewMsgCreateClient("goodchain", suite.header, trustingPeriod, 0, maxClockDrift, signer), false, "zero unbonding period passed"},
|
||||
{ibctmtypes.NewMsgCreateClient("goodchain", suite.header, trustingPeriod, ubdPeriod, maxClockDrift, nil), false, "Empty address passed"},
|
||||
{ibctmtypes.NewMsgCreateClient("goodchain", suite.header, trustingPeriod, ubdPeriod, maxClockDrift, nil), false, "Empty chain ID"},
|
||||
{ibctmtypes.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, 0, ubdPeriod, maxClockDrift, signer), false, "zero trusting period passed"},
|
||||
{ibctmtypes.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, trustingPeriod, 0, maxClockDrift, signer), false, "zero unbonding period passed"},
|
||||
{ibctmtypes.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, trustingPeriod, ubdPeriod, maxClockDrift, nil), false, "Empty address passed"},
|
||||
{ibctmtypes.NewMsgCreateClient(exported.ClientTypeTendermint, ibctmtypes.Header{}, trustingPeriod, ubdPeriod, maxClockDrift, signer), false, "nil header"},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
err := tc.msg.ValidateBasic()
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err, "Msg %d failed: %v", i, err)
|
||||
suite.Require().NoError(err, "Msg %d failed: %v", i, tc.errMsg)
|
||||
} else {
|
||||
suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ package types
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -15,6 +17,7 @@ import (
|
|||
channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
|
||||
commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported"
|
||||
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
|
||||
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
|
||||
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
|
||||
)
|
||||
|
||||
|
@ -63,6 +66,23 @@ func (cs ClientState) IsFrozen() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Validate performs a basic validation of the client state fields.
|
||||
func (cs ClientState) Validate() error {
|
||||
if err := host.DefaultClientIdentifierValidator(cs.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(cs.ChainID) == "" {
|
||||
return errors.New("chain id cannot be blank")
|
||||
}
|
||||
if cs.Height <= 0 {
|
||||
return fmt.Errorf("height must be positive: %d", cs.Height)
|
||||
}
|
||||
if cs.store == nil {
|
||||
return errors.New("KVStore cannot be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyClientConsensusState verifies a proof of the consensus
|
||||
// state of the loop-back client.
|
||||
// VerifyClientConsensusState verifies a proof of the consensus state of the
|
||||
|
|
|
@ -16,6 +16,44 @@ const (
|
|||
testSequence = 1
|
||||
)
|
||||
|
||||
func (suite *LocalhostTestSuite) TestValidate() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
clientState types.ClientState
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
name: "valid client",
|
||||
clientState: types.NewClientState(suite.store, "chainID", 10),
|
||||
expPass: true,
|
||||
},
|
||||
{
|
||||
name: "invalid chain id",
|
||||
clientState: types.NewClientState(suite.store, " ", 10),
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid height",
|
||||
clientState: types.NewClientState(suite.store, "chainID", 0),
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid store",
|
||||
clientState: types.NewClientState(nil, "chainID", 10),
|
||||
expPass: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := tc.clientState.Validate()
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err, tc.name)
|
||||
} else {
|
||||
suite.Require().Error(err, tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *LocalhostTestSuite) TestVerifyClientConsensusState() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
|
|
@ -21,7 +21,7 @@ func defaultIdentifierValidator(id string, min, max int) error {
|
|||
if strings.Contains(id, "/") {
|
||||
return sdkerrors.Wrapf(ErrInvalidID, "identifier %s cannot contain separator '/'", id)
|
||||
}
|
||||
// valid id must be between 10 and 20 characters
|
||||
// valid id must be between 9 and 20 characters
|
||||
if len(id) < min || len(id) > max {
|
||||
return sdkerrors.Wrapf(ErrInvalidID, "identifier %s has invalid length: %d, must be between %d-%d characters", id, len(id), min, max)
|
||||
}
|
||||
|
@ -33,10 +33,10 @@ func defaultIdentifierValidator(id string, min, max int) error {
|
|||
}
|
||||
|
||||
// DefaultClientIdentifierValidator is the default validator function for Client identifiers
|
||||
// A valid Identifier must be between 10-20 characters and only contain lowercase
|
||||
// A valid Identifier must be between 9-20 characters and only contain lowercase
|
||||
// alphabetic characters,
|
||||
func DefaultClientIdentifierValidator(id string) error {
|
||||
return defaultIdentifierValidator(id, 10, 20)
|
||||
return defaultIdentifierValidator(id, 9, 20)
|
||||
}
|
||||
|
||||
// DefaultConnectionIdentifierValidator is the default validator function for Connection identifiers
|
||||
|
|
|
@ -2,17 +2,20 @@ package ibc
|
|||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
client "github.com/cosmos/cosmos-sdk/x/ibc/02-client"
|
||||
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
|
||||
)
|
||||
|
||||
// GenesisState defines the ibc module's genesis state.
|
||||
type GenesisState struct {
|
||||
ClientGenesis client.GenesisState `json:"client_genesis" yaml:"client_genesis"`
|
||||
ConnectionGenesis connection.GenesisState `json:"connection_genesis" yaml:"connection_genesis"`
|
||||
}
|
||||
|
||||
// DefaultGenesisState returns the ibc module's default genesis state.
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
ClientGenesis: client.DefaultGenesisState(),
|
||||
ConnectionGenesis: connection.DefaultGenesisState(),
|
||||
}
|
||||
}
|
||||
|
@ -20,18 +23,24 @@ func DefaultGenesisState() GenesisState {
|
|||
// Validate performs basic genesis state validation returning an error upon any
|
||||
// failure.
|
||||
func (gs GenesisState) Validate() error {
|
||||
if err := gs.ClientGenesis.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gs.ConnectionGenesis.Validate()
|
||||
}
|
||||
|
||||
// InitGenesis initializes the ibc connection submodule's state from a provided genesis
|
||||
// InitGenesis initializes the ibc state from a provided genesis
|
||||
// state.
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, gs GenesisState) {
|
||||
client.InitGenesis(ctx, k.ClientKeeper, gs.ClientGenesis)
|
||||
connection.InitGenesis(ctx, k.ConnectionKeeper, gs.ConnectionGenesis)
|
||||
}
|
||||
|
||||
// ExportGenesis returns the ibc connection submodule's exported genesis.
|
||||
// ExportGenesis returns the ibc exported genesis.
|
||||
func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
return GenesisState{
|
||||
ClientGenesis: client.ExportGenesis(ctx, k.ClientKeeper),
|
||||
ConnectionGenesis: connection.ExportGenesis(ctx, k.ConnectionKeeper),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,47 @@
|
|||
package ibc
|
||||
package ibc_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
client "github.com/cosmos/cosmos-sdk/x/ibc/02-client"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
|
||||
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
|
||||
connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported"
|
||||
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
|
||||
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
|
||||
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
|
||||
)
|
||||
|
||||
var (
|
||||
connectionID = "connectionidone"
|
||||
clientID = "clientidone"
|
||||
connectionID2 = "connectionidtwo"
|
||||
clientID2 = "clientidtwo"
|
||||
)
|
||||
|
||||
func TestValidateGenesis(t *testing.T) {
|
||||
|
||||
func (suite *IBCTestSuite) TestValidateGenesis() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
genState GenesisState
|
||||
genState ibc.GenesisState
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
genState: DefaultGenesisState(),
|
||||
genState: ibc.DefaultGenesisState(),
|
||||
expPass: true,
|
||||
},
|
||||
{
|
||||
name: "valid genesis",
|
||||
genState: GenesisState{
|
||||
genState: ibc.GenesisState{
|
||||
ClientGenesis: client.NewGenesisState(
|
||||
[]exported.ClientState{
|
||||
ibctmtypes.NewClientState(clientID, trustingPeriod, ubdPeriod, maxClockDrift, suite.header),
|
||||
localhosttypes.NewClientState(suite.store, "chaindID", 10),
|
||||
},
|
||||
[]client.ClientConsensusStates{
|
||||
client.NewClientConsensusStates(
|
||||
clientID,
|
||||
[]exported.ConsensusState{
|
||||
ibctmtypes.NewConsensusState(
|
||||
suite.header.Time, commitmenttypes.NewMerkleRoot(suite.header.AppHash), suite.header.GetHeight(), suite.header.ValidatorSet,
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
ConnectionGenesis: connection.NewGenesisState(
|
||||
[]connection.ConnectionEnd{
|
||||
connection.NewConnectionEnd(connectionexported.INIT, connectionID, clientID, connection.NewCounterparty(clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))), []string{"1.0.0"}),
|
||||
|
@ -44,14 +53,31 @@ func TestValidateGenesis(t *testing.T) {
|
|||
},
|
||||
expPass: true,
|
||||
},
|
||||
{
|
||||
name: "invalid client genesis",
|
||||
genState: ibc.GenesisState{
|
||||
ClientGenesis: client.NewGenesisState(
|
||||
[]exported.ClientState{
|
||||
ibctmtypes.NewClientState(clientID, trustingPeriod, ubdPeriod, maxClockDrift, suite.header),
|
||||
localhosttypes.NewClientState(suite.store, "chaindID", 0),
|
||||
},
|
||||
nil,
|
||||
),
|
||||
ConnectionGenesis: connection.DefaultGenesisState(),
|
||||
},
|
||||
expPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid connection genesis",
|
||||
genState: GenesisState{
|
||||
genState: ibc.GenesisState{
|
||||
ClientGenesis: client.DefaultGenesisState(),
|
||||
ConnectionGenesis: connection.NewGenesisState(
|
||||
[]connection.ConnectionEnd{
|
||||
connection.NewConnectionEnd(connectionexported.INIT, connectionID, "CLIENTIDONE", connection.NewCounterparty(clientID, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))), []string{"1.0.0"}),
|
||||
},
|
||||
nil,
|
||||
[]connection.ConnectionPaths{
|
||||
connection.NewConnectionPaths(clientID, []string{ibctypes.ConnectionPath(connectionID)}),
|
||||
},
|
||||
),
|
||||
},
|
||||
expPass: false,
|
||||
|
@ -62,9 +88,9 @@ func TestValidateGenesis(t *testing.T) {
|
|||
tc := tc
|
||||
err := tc.genState.Validate()
|
||||
if tc.expPass {
|
||||
require.NoError(t, err, tc.name)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
} else {
|
||||
require.Error(t, err, tc.name)
|
||||
suite.Require().Error(err, tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package ibc_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/store/cachekv"
|
||||
"github.com/cosmos/cosmos-sdk/store/dbadapter"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
)
|
||||
|
||||
const (
|
||||
connectionID = "connectionidone"
|
||||
clientID = "clientidone"
|
||||
connectionID2 = "connectionidtwo"
|
||||
clientID2 = "clientidtwo"
|
||||
|
||||
trustingPeriod time.Duration = time.Hour * 24 * 7 * 2
|
||||
ubdPeriod time.Duration = time.Hour * 24 * 7 * 3
|
||||
maxClockDrift time.Duration = time.Second * 10
|
||||
)
|
||||
|
||||
type IBCTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
cdc *codec.Codec
|
||||
ctx sdk.Context
|
||||
app *simapp.SimApp
|
||||
store sdk.KVStore
|
||||
header ibctmtypes.Header
|
||||
}
|
||||
|
||||
func (suite *IBCTestSuite) SetupTest() {
|
||||
isCheckTx := false
|
||||
suite.app = simapp.Setup(isCheckTx)
|
||||
|
||||
privVal := tmtypes.NewMockPV()
|
||||
pubKey, err := privVal.GetPubKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
val := tmtypes.NewValidator(pubKey, 10)
|
||||
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val})
|
||||
|
||||
mem := dbadapter.Store{DB: dbm.NewMemDB()}
|
||||
suite.store = cachekv.NewStore(mem)
|
||||
suite.header = ibctmtypes.CreateTestHeader("chainID", 10, now, valSet, []tmtypes.PrivValidator{privVal})
|
||||
|
||||
suite.cdc = suite.app.Codec()
|
||||
suite.ctx = suite.app.BaseApp.NewContext(isCheckTx, abci.Header{})
|
||||
}
|
||||
|
||||
func TestIBCTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IBCTestSuite))
|
||||
}
|
Loading…
Reference in New Issue